【问题标题】:How to get all XML branches如何获取所有 XML 分支
【发布时间】:2014-04-27 07:41:56
【问题描述】:

如何使用 Java 获取所有 XML 分支。

例如,如果我有以下 XML:

<?xml version="1.0" encoding="UTF-8"?>
<addresses xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation='test.xsd'>
    <address>
        <name>Joe Tester</name>
        <street>Baker street 5</street>
    </address>
    <person>
        <name>Joe Tester</name>
        <age>44</age>
    </person>
</addresses>

我要获取以下分支:

addresses

addresses_address

addresses_address_name

addresses_address_street

addresses_person

addresses_person_name

addresses_person_age

谢谢。

【问题讨论】:

  • "所有使用 java 的 XML 分支"?
  • 有很多方法可以做到这一点。这取决于您打算如何处理数据。你想操纵节点吗?还是只提取文本?

标签: java xml xml-parsing


【解决方案1】:

您可以使用任何模板引擎轻松获取 XML 根、其节点和子节点名称。即 Velocity,FreeMarker 和其他,FreeMarker 具有强大的 XML 处理新工具。您可以将 XML 文档放入数据模型中,模板可以通过多种方式从其中提取数据,例如使用 XPath 表达式。 FreeMarker,作为一种 XML 转换工具,采用万维网联盟 (W3C) 颁布的大量 better-known XSLT stylesheet 方法。

FerMarker 支持 XPath 来使用 jaxen,XPath 表达式需要 Jaxen。 downlaod

FreeMarker 将使用 Xalan,除非您通过从 Java 调用 freemarker.ext.dom.NodeModel.useJaxenXPathSupport() 来选择 Jaxen。


只需要一个模板,它将根据输入的 XML 生成所有 XML 分支。真正将任何 XML 放在运行时数据模型 freemarker 将处理模板并生成对应于该 XML 结构的 XML 分支。如果您的 XML 结构将发生变化,则无需更改您的 Java 代码。即使您想更改输出,更改也会出现在模板文件中,因此无需重新编译 Java 代码。

只需更改模板,即时获取更改。

FTL 文件 [用于创建 xml 分支名称的多个 XML 文档的一个模板]

<#list doc ['/*' ] as rootNode>
  <#assign rootNodeValue="${rootNode?node_name}">
  ${rootNodeValue}
<#list doc ['/*/*' ] as childNodes>
  <#if childNodes?is_node==true>
      ${rootNodeValue}-${childNodes?node_name}
     <#list doc ['/*/${childNodes?node_name}/*' ] as subNodes>
      ${rootNodeValue}-${childNodes?node_name}-${subNodes?node_name}
     </#list>
  </#if>
</#list>
</#list>

用于流程模板的 XMLTest.Java

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import freemarker.ext.dom.NodeModel;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class XMLTest {

    public static void main(String[] args) throws SAXException, IOException,
            ParserConfigurationException, TemplateException {

        Configuration config = new Configuration();
        config.setClassForTemplateLoading(XMLTest.class, "");
        config.setObjectWrapper(new DefaultObjectWrapper());
        config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);

        Map<String, Object> dataModel = new HashMap<String, Object>();
              //load xml
        InputStream stream = XMLTest.class.getClassLoader().getResourceAsStream(xml_path);
            // if you xml sting then then pass it from InputSource constructor, no need of load xml from dir
        InputSource source = new InputSource(stream);
        NodeModel xmlNodeModel = NodeModel.parse(source);
        dataModel.put("doc", xmlNodeModel);
        Template template = config.getTemplate("test.ftl");

        StringWriter out = new StringWriter();
        template.process(dataModel, out);
        System.out.println(out.getBuffer().toString());

    }

}

最终输出

addresses
      addresses-address
      addresses-address-name
      addresses-address-street
      addresses-person
      addresses-person-name
      addresses-person-age

查看1.XML Node Model2.XML Node MOdel的文档

here 下载 FreeMarker 从here下载Jaxen

【讨论】:

    【解决方案2】:

    您可以通过多种方式从 XML 中提取数据并在 Java 中使用它。您选择哪一种取决于您希望如何使用数据。

    一些场景是:

    1. 您可能想要操作节点、排序、删除和添加其他节点以及转换 XML。
    2. 您可能只想阅读(并可能更改)元素和属性中包含的文本。
    3. 您可能有一个非常大的文件,而您只想找到一些特定数据而忽略文件的其余部分。

    对于场景 #3,最好的选择是一些内存效率高的基于流的解析器,例如 SAX 或带有 StAX 的 XML 阅读器> API。

    如果您主要阅读(而不是写作),您也可以将其用于场景 #2,但基于 DOM 的 API 可能更易于使用。您可以使用标准的 DOM org.w3c.dom API 或更类似于 Java 的 API,例如 JDOMDOM4J。如果您希望将 XML 文件与 Java 对象同步,您可能还希望使用完整的 Java-XML 映射 框架,例如 JAXB

    DOM API 也适用于 场景 #1,但在许多情况下,使用 XSLT 可能更简单(通过 javax.xml.transform TrAX强> Java 中的 API)。如果你使用 DOM,你也可以使用 XPath 来选择节点。

    我将向您展示如何使用标准 DOM API (org.w3c.dom) 和 XPath (javax.xml.xpath) 提取文件的各个节点的示例。

    1.设置

    初始化解析器:

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    

    将文件解析为文档对象模型:

    Document source = builder.parse(new File("src/main/resources/addresses.xml"));
    

    2。使用 J2SE DOM 选择节点

    你使用getDocumentElement()获取根元素:

    Element addresses = source.getDocumentElement();
    

    从那里您可以使用getChildNodes() 获取子节点,但这将返回所有 子节点,其中包括文本节点(元素之间的空白)。 addresses.getChildNodes().item(0) 返回&lt;addresses&gt; 标记之后和&lt;address&gt; 标记之前的空格。要获得该元素,您必须选择第二项。更简单的方法是使用getElementsByTagName,它返回一个节点集并获取第一项:

    Element addresses_address = (Element)addresses.getElementsByTagName("address").item(0);
    

    许多 DOM 方法返回 org.w3c.dom.Node 对象,您必须对其进行强制转换。有时它们可​​能不是 Element 对象,因此您必须检查。节点集不会自动转换为数组。它们是org.w3c.dom.NodeList,因此您必须使用.item(0) 而不是[0](如果您使用其他DOM API,例如JDOM 或DOM4J,它看起来会更直观)。

    您可以使用addresses.getElementsByTagName 来获取您需要的所有元素,但您必须处理两个&lt;name&gt; 元素的上下文。所以更好的方法是在适当的上下文中调用它:

    Element addresses_address        = (Element)addresses.getElementsByTagName("address").item(0);
    Element addresses_address_name   = (Element)addresses_address.getElementsByTagName("name").item(0);
    Element addresses_address_street = (Element)addresses_address.getElementsByTagName("street").item(0);
    
    Element addresses_person      = (Element)addresses.getElementsByTagName("person").item(0);
    Element addresses_person_name = (Element)addresses_person.getElementsByTagName("name").item(0);
    Element addresses_person_age  = (Element)addresses_person.getElementsByTagName("age").item(0);
    

    这将为您的文件提供所有Element 节点(或您所称的分支)。如果您想要文本节点(作为实际的 Node 对象),您需要将其作为第一个子节点:

    Node textNode = addresses2_address_street.getFirstChild();
    

    如果你想要String 的内容,你可以使用:

    String street = addresses2_address_street.getTextContent();
    

    3.使用 XPath 选择节点

    另一种选择节点的方法是使用 XPath。您将需要 DOM 源,还需要初始化 XPath 处理器:

    XPath xPath = XPathFactory.newInstance().newXPath();
    

    你可以像这样提取根节点:

    Element addresses = (Element)xPath.evaluate("/addresses", source, XPathConstants.NODE);
    

    所有其他节点都使用类似路径的语法:

    Element addresses_address        = (Element)xPath.evaluate("/addresses/address", source, XPathConstants.NODE);
    Element addresses_address_name   = (Element)xPath.evaluate("/addresses/address/name", source, XPathConstants.NODE);
    Element addresses_address_street = (Element)xPath.evaluate("/addresses/address/street", source, XPathConstants.NODE);
    

    您也可以使用相对路径,选择不同的元素作为根:

    Element addresses_person      = (Element)xPath.evaluate("person", addresses, XPathConstants.NODE);
    Element addresses_person_name = (Element)xPath.evaluate("person/name", addresses, XPathConstants.NODE);
    Element addresses_person_age  = (Element)xPath.evaluate("age", addresses_person, XPathConstants.NODE);
    

    你可以像以前一样获取文本内容,因为你有Element对象:

    String addressName = addresses_address_name.getTextContent();
    

    但是你也可以直接使用上面相同的方法而不用最后一个参数(默认为字符串)。这里我使用了不同的相对和绝对 XPath 表达式:

    String addressName   = xPath.evaluate("name", addresses_address);
    String addressStreet = xPath.evaluate("address/street", addresses);
    String personName    = xPath.evaluate("name", addresses_person);
    String personAge     = xPath.evaluate("/addresses/person/age", source);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-15
      • 1970-01-01
      • 2014-11-02
      • 2019-11-02
      • 2019-05-30
      • 1970-01-01
      • 2011-02-10
      • 2019-08-05
      相关资源
      最近更新 更多