JAXB.unmarshal()提取XML节点的最简单方法是什么?

5
我使用的目标从WSDL生成Java代码。然后在我的测试中,我使用JAXB.unmarshal()方法从原始Web服务XML结果中填充类。
一个典型的例子是GetAllResponseType response = unmarshal("get-all.xml", GetAllResponseType.class),使用以下方法:
<T> T unmarshal(String filename, Class<T> clazz) throws Exception {
    InputStream body = getClass().getResourceAsStream(filename);
    return javax.xml.bind.JAXB.unmarshal(body, clazz);
}

问题在于: 原始的XML响应总是有封闭的Envelope和Body标签,但wsdl2java不会生成这些标签作为类:
<n4:Envelope xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:n="http://www.informatica.com/wsdl/"
         xmlns:n4="http://schemas.xmlsoap.org/soap/envelope/" xmlns:n5="http://schemas.xmlsoap.org/wsdl/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <n4:Body>
    <n:getAllResponse xmlns:n="http://www.informatica.com/wsdl/">
        <n:getAllResponseElement>
           ...
        </n:getAllResponseElement>
    </n:getAllResponse>
  </n4:Body>
</n4:Envelope>

为了使用JAXB.unmarshal(),我需要做以下几件事情:
  1. 要么手动去掉get-all.xml文件中的Envelope/Body标签
  2. 要么提取getAllResponse节点并重新将其转换为InputStream
  3. 要么创建Envelope和Body类
目前我采取的是第2种方法,但需要写很多代码。
<T> T unmarshal(String filename, Class<T> clazz) throws Exception {
    InputStream is = getClass().getResourceAsStream(filename);
    InputStream body = nodeContent(is, "n4:Body");
    return javax.xml.bind.JAXB.unmarshal(body, clazz);
}

InputStream nodeContent(InputStream is, String name) throws Exception {
    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
    Document doc = docBuilder.parse(is);
    Node node = firstNonTextNode(doc.getElementsByTagName(name).item(0).getChildNodes());
    return nodeToStream(node);
}

Node firstNonTextNode(NodeList nl) {
    for (int i = 0; i < nl.getLength(); i++) {
        if (!(nl.item(i) instanceof Text)) {
            return nl.item(i);
        }
    }
    throw new RuntimeException("Couldn't find nontext node");
}

InputStream nodeToStream(Node node) throws Exception {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Source xmlSource = new DOMSource(node);
    Result outputTarget = new StreamResult(outputStream);
    TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
    return new ByteArrayInputStream(outputStream.toByteArray());
}

我的问题是:

  • 2中,是否有更简单的提取方法?我很想试试用正则表达式。我尝试过XPath,但不知道怎么操作。提供代码示例会很有帮助。
  • 是否可以让wsdl2java创建Body / Envelope类(3),或者自己创建它们容易吗?
3个回答

4
使用 DOMSource 将节点作为输入传递。下面的方法将 org.w3c.dom.Node 作为输入,并返回反序列化后的类。
private <T> T unmarshal(Node node, Class<T> clazz) throws JAXBException {
        XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
        Source xmlSource = new DOMSource(node);
        Unmarshaller unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
        return unmarshaller.unmarshal(xmlSource, clazz).getValue();
}

2

n4:Body节点内的节点可以通过使用XMLStreamReader和“原始”JAXB Unmarshaller进行反编组:

<T> T unmarshal(String filename, Class<T> clazz) throws Exception {
    XMLInputFactory xif = XMLInputFactory.newFactory();
    XMLStreamReader xsr = xif.createXMLStreamReader(getClass().getResourceAsStream(filename));
    xsr.nextTag();
    while (!xsr.getLocalName().equals("Body")) {
        xsr.nextTag();
    }
    xsr.nextTag();
    Unmarshaller unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
    return unmarshaller.unmarshal(xsr, clazz).getValue();
}

感谢Blaise Doughan在本答案中提供的帮助。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接