如何从 XML 中获取整个标记作为字符串

How to get entire tag as String from an XML

提问人:sita 提问时间:4/6/2023 最后编辑:sita 更新时间:4/6/2023 访问量:93

问:

这里的问题是我每次都会得到具有不同命名空间的不同 xml。

我必须将一个标签读取为字符串并将其传递给另一个服务。

假设我有一次得到这个xml

<?xml version="1.0" encoding="utf-8" ?>
<inventory>
<header>
    <id>123</id>
</header>
 <book>
    <title>Snow Crash</title>
    <author>Neal Stephenson</author>
    <publisher>Spectra</publisher>
    <isbn>0553380958</isbn>
    <price>14.95</price>
 </book>
</inventory>

我也得到了这样的xml。命名空间将有所不同。它只是一个例子。

<?xml version="1.0" encoding="utf-8" ?>
<Category xmlns:in="uri.category.xsd.in.01">
<in:type>books</in:type>
<h:header xmlns:h="uri.header.xsd.01">
    <h:id>123</h:id>
    <h:memId>123</h:memId>
</h:header>
 <b:book xmlns:b="uri.books.xsd.01">
    <b:title>Snow Crash</b:title>
    <b:author>Neal Stephenson</b:author>
    <b:publisher>Spectra</b:publisher>
    <b:isbn>0553380958</b:isbn>
    <b:price>14.95</b:price>
 </b:book>
</Category>

注意:每次我都会得到不同的xml,有些带有命名空间,有些没有。但唯一共同点是这两个标签。就像上面的例子标题和书一样。

如果我得到第一个xml,我会像这样发送到另一个服务

<header>
    <id>123</id>
</header>

如果我得到第二个xml作为输入,那么我应该将其发送到另一个服务

<h:header xmlns:h="uri.header.xsd.01">
    <h:id>123</h:id>
    <h:memId>123</h:memId>
</h:header>

注意:此命名空间仅供参考。现在我得到了这个命名空间。我可能会得到具有不同命名空间的 xml,只有标题和书牌标签是通用的,而不是命名空间。以下内容可能会因不同的 xml 而更改。

xmlns:h="uri.header.xsd.01"

我已经使用 DOM 解析器和 xpath 以某种方式解决了这个问题。

我编写了一个方法来获取命名空间,如上所述,它是“h:”,并对字符串进行一些操作,如下所示。我想知道是否有更好的方法可以做到这一点。

public static String getNamespace(String s, Document doc) throws Exception{
    String ns="";
    XPath xpath = XPathFactory.newInstance().newXPath();
    NodeList nodeList = (NodeList) xpath.evaluate(s,doc, XPathConstants.NODESET);
    Element element = (Element) nodeList.item(0);
    String elementwithNS = element.toString().substring(1,element.toString().length()-1);
    String namespace[]=elementwithNS.split(":");
    if(namespace.length==3)
        ns= namespace[0]+":";
    return ns;
}

ns_Header = getNamespace("//*[local-name()='header']");//I get the namespace as h:if it is empty then empty string 
String header_close_tag = "</"+ns_Header+"header>"
String header = StringUtils.substringBetween(xml,"header",header_close_tag);
String header_tag = "<"+ns_Header+"header"+header+header_close_tag;

我还想读取标头标签值,例如 id 和 memId。我能够在没有 namepsace 的情况下做到这一点,但是当命名空间也添加它时,命名空间会随着不同的 xml 而不断变化。我不确定如何读取标签值。不想使用 JAXB,因为我使用的 XML 非常大,我最终会基于不同的 XML 创建多个 POJO。

Java XML DOM XPath

评论


答:

1赞 vanje 4/6/2023 #1

无需提取实际命名空间。如果从 XPath 表达式中获取标头元素,则命名空间仍然存在。您只需将节点序列化为字符串即可。

下面是一个完整的示例:

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import java.io.StringReader;
import java.io.StringWriter;

public class XmlExample {
  private static final String xmlWithoutNs = "<inventory>\n" +
    "<header>\n" +
    "    <id>123</id>\n" +
    "</header>\n" +
    " <book>\n" +
    "    <title>Snow Crash</title>\n" +
    "    <author>Neal Stephenson</author>\n" +
    "    <publisher>Spectra</publisher>\n" +
    "    <isbn>0553380958</isbn>\n" +
    "    <price>14.95</price>\n" +
    " </book>\n" +
    "</inventory>";

  private static final String xmlWithNs = "<Category xmlns:in=\"uri.category.xsd.in.01\">\n" +
    "<in:type>books</in:type>\n" +
    "<h:header xmlns:h=\"uri.header.xsd.01\">\n" +
    "    <h:id>123</h:id>\n" +
    "    <h:memId>123</h:memId>\n" +
    "</h:header>\n" +
    " <b:book xmlns:b=\"uri.books.xsd.01\">\n" +
    "    <b:title>Snow Crash</b:title>\n" +
    "    <b:author>Neal Stephenson</b:author>\n" +
    "    <b:publisher>Spectra</b:publisher>\n" +
    "    <b:isbn>0553380958</b:isbn>\n" +
    "    <b:price>14.95</b:price>\n" +
    " </b:book>\n" +
    "</Category>";

  private static String xmlToString(Node node) throws TransformerException {
    TransformerFactory fac = TransformerFactory.newInstance();
    Transformer transformer;
    transformer = fac.newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    StringWriter writer = new StringWriter();
    transformer.transform(new DOMSource(node), new StreamResult(writer));
    return writer.toString();
  }

  private static String getHeaderAsString(Document doc) throws XPathExpressionException, TransformerException {
    XPath xpath = XPathFactory.newInstance().newXPath();
    XPathExpression expr = xpath.compile("/*/*[local-name() = 'header']");
    Node node = (Node) expr.evaluate(doc, XPathConstants.NODE);
    return xmlToString(node);
  }

  public static void main(String[] args) throws Exception {
    DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
    fac.setNamespaceAware(true);
    DocumentBuilder builder = fac.newDocumentBuilder();
    Document docWithNs = builder.parse(new InputSource(new StringReader(xmlWithNs)));
    System.out.println("Example with Namespace:");
    System.out.println(getHeaderAsString(docWithNs));

    Document docWithoutNs = builder.parse(new InputSource(new StringReader(xmlWithoutNs)));
    System.out.println("\nExample without Namespace:");
    System.out.println(getHeaderAsString(docWithoutNs));
  }
}

这是输出:

Example with Namespace:
<h:header xmlns:h="uri.header.xsd.01">
    <h:id>123</h:id>
    <h:memId>123</h:memId>
</h:header>

Example without Namespace:
<header>
    <id>123</id>
</header>

评论

0赞 sita 4/6/2023
我不知道节点到字符串的序列化。这是一个更好的代码。谢谢!