【问题标题】:Unmarshalling an XML using Xpath expression and jaxb使用 Xpath 表达式和 jaxb 解组 XML
【发布时间】:2013-06-13 00:44:53
【问题描述】:

我是 JAXB 的新手,想知道是否有一种方法可以将 XML 解组到我的响应对象,但使用 xpath 表达式。问题是我正在调用第三方网络服务,而我收到的响应有很多细节。我不希望将 XML 中的所有细节都映射到我的响应对象。我只想从 xml 映射一些细节,我可以使用这些细节使用特定的 XPath 表达式并将它们映射到我的响应对象。是否有注释可以帮助我实现这一目标?

例如考虑以下响应

<root>
  <record>
    <id>1</id>
    <name>Ian</name>
    <AddressDetails>
      <street> M G Road </street>
    </AddressDetails>
  </record>  
</root>

我只对检索街道名称感兴趣,所以我想使用 xpath 表达式使用“root/record/AddressDetails/street”获取街道的值并将其映射到我的响应对象

public class Response{
     // How do i map this in jaxb, I do not wish to map record,id or name elements
     String street; 

     //getter and setters
     ....
}   

谢谢

【问题讨论】:

  • 到目前为止你做了什么?
  • 从我的发现看来我必须使用 eclipseLink MOXy 来实现这一目标

标签: java xpath jaxb unmarshalling


【解决方案1】:

注意:我是EclipseLink JAXB (MOXy) 领导,也是JAXB (JST-222) 专家组的成员。

对于这个用例,您可以使用 MOXy 的 @XmlPath 扩展。

回应

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement(name="root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Response{
    @XmlPath("record/AddressDetails/street/text()")
    String street; 

    //getter and setters
}

jaxb.properties

要将 MOXy 用作您的 JAXB 提供程序,您需要在与域模型相同的包中包含一个名为 jaxb.properties 的文件,其中包含以下条目(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Response.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum17141154/input.xml");
        Response response = (Response) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(response, System.out);
    }

}

输出

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <record>
      <AddressDetails>
         <street> M G Road </street>
      </AddressDetails>
   </record>
</root>

更多信息

【讨论】:

  • blaise,我喜欢使用@XmlPath。是否有一个 JSR 注释可以做类似的事情但没有将我绑定到 org.eclipse。谢谢
  • MOXy 给 404 :(
【解决方案2】:

如果您想要的只是街道名称,只需使用 XPath 表达式将其作为字符串获取,然后忘记 JAXB - 复杂的 JAXB 机制不会添加任何值。

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

public class XPathDemo {

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

        InputSource xml = new InputSource("src/forum17141154/input.xml");
        String result = (String) xpath.evaluate("/root/record/AddressDetails/street", xml, XPathConstants.STRING);
        System.out.println(result);
    }

}

【讨论】:

  • @Blaise 当我尝试使用上述代码时,我得到 java.net.MalformedURLException: no protocol: error。就在上面带有上述xml的代码。
  • “new InputSource()”的规范说提供的 SystemId 必须是绝对 URI,而不是本示例中提供的相对 URI。
【解决方案3】:

如果您想映射到 XML 文档的中间,您可以执行以下操作:

演示代码

您可以使用 StAX 解析器前进到您希望解组的文档部分,然后从那里解组 XMLStreamReader

import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Response.class);

        XMLInputFactory xif = XMLInputFactory.newFactory();
        StreamSource xml = new StreamSource("src/forum17141154/input.xml");
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);
        while(!(xsr.isStartElement() && xsr.getLocalName().equals("AddressDetails"))) {
            xsr.next();
        }

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        JAXBElement<Response> response = unmarshaller.unmarshal(xsr, Response.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(response, System.out);
    }

}

回应

域对象上的映射是相对于您要映射到的 XML 文档的片段进行的。

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Response{

    String street; 

    //getter and setters
}

输出

<?xml version="1.0" encoding="UTF-8"?>
<AddressDetails>
   <street> M G Road </street>
</AddressDetails>

更多信息

【讨论】:

  • 是否需要创建一个单独的 jaxb 域对象,或者我可以从完整的 xsd 重用通过 xjc 创建的那些(尤其是在提取完整文档树的较大部分时)?
  • @MRalwasser - 您可以使用从 XML Schema 生成的那些。
  • 谢谢,这就是我目前正在做的事情 - 你知道为什么我的解组器会创建所需模型的实例但没有将任何子元素映射到其中吗?
  • @MRalwasser - 如果您将要解组的文档部分复制到其自己的 XML 文档中(根据需要修复命名空间声明),则可以正确解组。这将有助于确认您的映射是否正确。
  • 我这样做了 - 似乎 xml 部分的顶级元素是“意外”元素。也许原因是它既不是根元素,也不是在 ObjectFactory 中定义为 @XmlElementDecl 注释的 create...() 方法。我有哪些选择?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
  • 2013-03-26
  • 1970-01-01
相关资源
最近更新 更多