使用JAXB解析Xml叶节点元素值

时间:2013-12-20 13:09:30

标签: java xml-parsing jaxb unmarshalling xmlnode

我有一个xsd说request.xsd和相应的jaxb生成的类。现在我得到了一个xml文件request.xml,我可以解组并创建“请求”对象。

我在xml中有很多元素标签,其中一些可以多次使用。我需要创建一个java.util.List,它应该包含所有叶节点值。

例如:

下面是我的request.xml:

<Request>
  <Operation>manual</Operation>
  <Work>
     <WorkModule>
          <Name>AXN</Name>
     </WorkModule>
  </Work>
  <Identifier>
     <WorkStatus>
          <WorkName>CCH</WorkName>
     </WorkStatus>
     <WorkStatus>
          <WorkName>TMH</WorkName>
     </WorkStatus>
  </Identifier>
</Request>

下面是我的JAXB生成的Request类。 Simillarly还有其他类对应于每个xml元素:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "Operation",
    "Work",
    "Identifier"
})
@XmlRootElement(name = "Request", namespace = "http://www.sprts.com/clm/nso/mahsgd")
public class Request{

    @XmlElement(name = "Operation", required = true)
    protected Operation operation;
    @XmlElement(name = "Work", required = true)
    protected Work work;
    @XmlElement(name = "Identifier", required = true)
    protected Identifier identifier;

    \\ getters and setters
}

因此,使用JAXB,我可以获得具有xml文件中所有值的unmarshalled Request对象。

现在,如何在不使用请求对象的getter的情况下以通用方式获取所有叶节点值(operation,name,workName),然后我可以将其放入某个集合中,例如List。我听说DOM被用来做类似的东西,但我需要使用JAXB。

(不使用来自请求对象的getter   String opertaion = request.getOperation();String name = request.getWork().getWorkModule().getName();

- 编辑 -

有人可以帮助我找到最佳解决方案。如果问题陈述不清楚,请告诉我。

- EDIT-- 与Doughan&amp;亚历山德罗斯的帮助和一些人能够达到同样的目的。不确定是否解决(将JAXB对象转换为DOM对象到InputSource)是最佳解决方案。以下是工作代码。

     JAXBContext jc = JAXBContext.newInstance(JAXBObject.class);

     // Create the Document
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     DocumentBuilder db = dbf.newDocumentBuilder();
     Document document = db.newDocument();

     // Marshal the Object to a Document
     Marshaller marshaller = jc.createMarshaller();
     marshaller.marshal(jaxbObject, document);

    XPathFactory xpf = XPathFactory.newInstance();
    XPath xp = xpf.newXPath();

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Source xmlSource = new DOMSource(document);
    Result outputTarget = new StreamResult(outputStream);
    TransformerFactory.newInstance().newTransformer().transform(xmlSource,outputTarget);
    InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
    InputSource source = new InputSource(is);

    NodeList leafNodeObjects = (NodeList) xp.evaluate("//*[not(*)]", source, XPathConstants.NODESET);

    for(int x=0; x<leafNodeObjects.getLength(); x++) {
                    System.out.print("nodeElement = ");
                    System.out.print(leafNodeObjects.item(x).getNodeName());
                    System.out.print(" and node value = ");
                    System.out.println(leafNodeObjects.item(x).getTextContent());
                    inputDtos.add(new InputDto(leafNodeObjects.item(x).getNodeName(),
                            leafNodeObjects.item(x).getTextContent()));
   }

3 个答案:

答案 0 :(得分:2)

来自您的赏金评论:

  

我想创建一个NodeObject列表,其中NodeObject具有nodeElement   和nodeValue属性。 〔实施例。如果我有一个像这样的元素   然后我将为此元素提供一个NodeObject   with nodeElement = name和nodeValue = property。

您可以使用以下XPath从任何XML文档中获取叶节点(请参阅:How to select all leaf nodes using XPath expression?):

//*[not(*)]

此处使用javax.xml.xpath API:

import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        XPathFactory xpf = XPathFactory.newInstance();
        XPath xp = xpf.newXPath();

        InputSource xml = new InputSource("input.xml");
        NodeList leafNodeObjects = (NodeList) xp.evaluate("//*[not(*)]", xml, XPathConstants.NODESET);

        for(int x=0; x<leafNodeObjects.getLength(); x++) {
            System.out.print("nodeElement = ");
            System.out.print(leafNodeObjects.item(x).getNodeName());
            System.out.print(" and node value = ");
            System.out.println(leafNodeObjects.item(x).getTextContent());
        }
    }

}

以下是运行此演示代码的输出:

nodeElement = Operation and node value = manual
nodeElement = Name and node value = AXN
nodeElement = WorkName and node value = CCH
nodeElement = WorkName and node value = TMH

答案 1 :(得分:1)

我的问题是:您是否可以更改XSD以满足您的需求,或者XSD是否由其他人控制,您必须按原样使用它?

这很重要,因为JAXB的工作方式。基本上,JAXB将XSD转换为Java类。它也可以反过来(将Java类转换为XSD)。这里将详细介绍这种关系:http://docs.oracle.com/cd/E21764_01/web.1111/e13758/data_types.htm

在你的情况下,我假设有人写了一个你用来生成Java类的XSD,但是这些类有很多:“Something getSomething1(),Something getSomething2(),... Something getSomethingN()方法当你想要一个List getListOfSomethings()方法时。

有两种方法可以解决这个问题:

(1)更改XSD,以便“somethings”是复杂类型的一部分,这是一个序列(或任何会导致JAXB为列表生成getter的东西,根据我的原始答案)。

这并不总是可行的。如果XSD由某个外部实体控制,该实体表示“这就是我的数据,你必须忍受它,否则你的应用程序将无法读取我的数据”,那么你就无法做到这一点。为了给您一个具体的例子,假设您的应用程序想要从美国国会图书馆读取EAD数据。它的XSD在这里:http://www.loc.gov/ead/eadschema.html。这个XSD就是这样。你无法改变它。如果您更改它,您的应用程序将使用您对不同数据的定义。您必须考虑下面的方法(2),因为您无法控制XSD。

(2)不要使用JAXB。而是使用允许您查询元素的XML API。通过这种方式,您可以使用(例如)XPath查询收集所有“Somethings”(请参阅​​http://docs.oracle.com/javase/tutorial/jaxp/xslt/xpath.html)。

您可以创建一个加载XML的包装类,并使用List getSomethings()方法。这将是以下几点:

public class RequestWrapper {
    Document doc;
    public RequestWrapper(String xmlUri) {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        this.doc = builder.parse(xmlUri);
    }

    public List<Something> getSomethings() {
        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();
        XPathExpression expr = xpath.compile(<DEFINE A SUITABLE EXPRESSION>);
        NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

        List<Something> somethings = new LinkedList<Something>();
        // loop over the nodelist creating instances of Something
        return somethings;
    }
}

这是一个很好的教程,可以使用带有Stax的XPath,它可以派上用场:http://www.vogella.com/articles/JavaXML/article.html

(3)如果您愿意放弃标准Java API,可以考虑使用一个库来控制对Java的绑定,例如Castor:http://castor.codehaus.org/xml-framework.html

最终,您的问题是数据以不方便的方式呈现,在这种情况下您必须执行(2)或(3),或者您已经定义了不方便的XSD,在这种情况下您必须执行(1)

答案 2 :(得分:0)

您是在定义XML的结构,还是有人为您提供了必须使用的固定XSD?

如果您实际定义了请求XML的结构,则可以使用注释(如@XmlElement和@XmlElementWrapper)使JAXB与集合一起使用。有关详情,请参阅此处:http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html