XML API 或开源 jar,用于根据给定路径提取 XML 的子集

XML API or open source jar for extract a subset of XML based on a given path

我想知道是否有 API 或开源 jar 可以根据给定路径提取 XML 的子集。

例如: 我有一个XML,它是一个骨架(阴模型,由阳模型转换而来)

<xml .....>
<data>
   <model1>
     <element1>
        <id />
        <name />
        <address />
     </element1>
   </model1>
   <model2>
     <element2>
        <uid />
        <something />
     </element2>
   </model2>
   ....
</data>

给定路径:

data/model1/element1[id='1']/name  and  name value is 'John'

我希望返回以下内容

<xml .....>
<data>
   <model1>
     <element1>
        <id>1</id>
        <name>John</name>
     </element1>
   </model1>
<data>

我不太确定要搜索什么关键字。希望有足够了解 XML 的人可以提出建议。

另一个问题是,如果没有现成的 API 或开源,处理这个问题的最佳方法是什么?我应该使用 DOM 因为我需要骨架中的整个(树)结构吗?除了 DOM 占用太多内存外,其他副作用是什么?

您可以使用内置包javax.xml来读写数据。您可以使用 XML path language (XPath) 查询 XML。比如提取<element1>:

的子树
/data/model1/element1

或提取 <element1> 的子树,其中 child-elements <id> 有文本“1”:

/data/model1/element1[id/text() = 1] 

我写了一个小程序来演示用法。你需要

  • 创建 org.w3c.dom.Document
  • 将 XML 内容解析到此对象中
  • 编译您的 XPath 表达式
  • 使用编译后的 xpath 作为 NodeList
  • 提取文档
  • 导出 NodeList 或执行任何其他需要的任务。

您可以按如下方式编译程序和运行:

$ javac Demo.java
$ java Demo /data/model1/element1
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<data>
  <model1>
    <element1>

      <id>1</id>

      <name>John</name>

      <address>xxx</address>

    </element1>
    <element1>

      <id>2</id>

      <name>Tom</name>

      <address>yyy</address>

    </element1>
  </model1>
</data>

~ $ java Demo '/data/model1/element1[id/text() = 1]'
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<data>
  <model1>
    <element1>

      <id>1</id>

      <name>John</name>

      <address>xxx</address>

    </element1>
  </model1>
</data>

完整程序:

import java.io.*;
import java.nio.charset.StandardCharsets;
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 org.w3c.dom.*;

public class Demo {

  private static final String XML =
      "<?xml version=\"1.0\"?>\n"
          + "<data>\n"
          + "  <model1>\n"
          + "    <element1>\n"
          + "      <id>1</id>\n"
          + "      <name>John</name>\n"
          + "      <address>xxx</address>\n"
          + "    </element1>\n"
          + "    <element1>\n"
          + "      <id>2</id>\n"
          + "      <name>Tom</name>\n"
          + "      <address>yyy</address>\n"
          + "    </element1>\n"
          + "  </model1>\n"
          + "  <model2>\n"
          + "    <element2>\n"
          + "      <uid />\n"
          + "      <something />\n"
          + "    </element2>\n"
          + "  </model2>"
          + "</data>";

  public static void main(String[] args) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    Document source;
    try (InputStream in = new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8))) {
      source = factory.newDocumentBuilder().parse(in);
    }

    // Extract
    XPath xPath = XPathFactory.newInstance().newXPath();
    XPathExpression expr = xPath.compile(args[0]);

    NodeList nodeList = (NodeList) expr.evaluate(source, XPathConstants.NODESET);

    // Export
    Document target = factory.newDocumentBuilder().newDocument();
    Element data = target.createElement("data");
    Element model1 = target.createElement("model1");
    data.appendChild(model1);
    target.appendChild(data);
    for (int i = 0; i < nodeList.getLength(); i++) {
      Node node = nodeList.item(i);
      Node newNode = target.importNode(node, true);
      model1.appendChild(newNode);
    }
    System.out.println(getStringFrom(target));
  }

  private static String getStringFrom(Document doc) throws TransformerException {
    DOMSource domSource = new DOMSource(doc);
    StringWriter writer = new StringWriter();
    StreamResult result = new StreamResult(writer);
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer transformer = tf.newTransformer();
    // set indent
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
    transformer.transform(domSource, result);
    return writer.toString();
  }
}