如何获取所有XML分支

时间:2014-04-27 07:41:57

标签: java xml xml-parsing

如何使用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

感谢。

2 个答案:

答案 0 :(得分:0)

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

FrerMarker支持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());

    }

}

最终OutPut

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

请参阅1.XML Node Model 2.XML Node MOdel

的文档

here下载FreeMarker 来自here

的Downlaod Jaxen

答案 1 :(得分:0)

有许多方法可以从XML中提取数据并在Java中使用它。您选择的那个将取决于您想要如何使用数据。

有些情况是:

  1. 您可能希望操纵节点,排序,删除和添加其他节点以及转换XML。
  2. 您可能只想阅读(并可能更改)元素和属性中包含的文本。
  3. 你可能有一个非常大的文件,你只想找到一些特定的数据并忽略文件的其余部分。
  4. 对于场景#3 ,最好的选择是一些内存高效的基于流的解析器,例如 SAX 或带有 StAX API。

    如果你主要阅读(而不是写作),你也可以将它用于场景#2 ,但基于DOM的API可能更容易使用。您可以使用标准的 DOM org.w3c.dom API或类似Java的API,例如 JDOM DOM4J 。如果您希望将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)会在<addresses>标记之后和<address>标记之前返回空格。要获得元素,您必须选择第二个项目。更简单的方法是使用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来获取所需的所有元素,但是您必须处理两个<name>元素的上下文。所以更好的方法是在适当的上下文中调用它:

    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();
    

    但是你也可以使用上面相同的方法直接完成它而没有最后一个参数(默认为string)。这里我使用不同的相对和绝对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);