Jaxb:在同一个包中解组具有多个命名空间的 xml

Posted

技术标签:

【中文标题】Jaxb:在同一个包中解组具有多个命名空间的 xml【英文标题】:Jaxb: Unmarshalling xml with multiple namespaces in same package 【发布时间】:2013-03-24 05:47:26 【问题描述】:

我是在 xml 中使用命名空间的新手,所以我有点困惑,想澄清一下。我有一个 java 服务,我正在接收具有许多不同名称空间的 xml 文档,当我让它工作时,我觉得我一定做错了什么,所以我想检查一下。在我的 package-info.java 中,我有我的模式注释,例如:

@javax.xml.bind.annotation.XmlSchema(
    xmlns=
        @javax.xml.bind.annotation.XmHS(prefix="train", namespaceURI="http://mycompany/train"), 
        @javax.xml.bind.annotation.XmHS(prefix="passenger", namespaceURI="http://mycompany/passenger")
    , 
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm=QUALIFIED
)

我有一个在类级别注释的 Train.java:

@XmlRootElement(name="Train", namespace="http://mycompany/train")

类中的每个字段都用以下注释:

@XmlElement(name="Color") for example

Train 包含一个乘客列表,因此有一个属性

private Set<Passenger> passengers;

并且这个集合带有注释:

@XmlElementWrapper(name="Passengers")
@XmlElements(@XmlElement(name="Passenger", namespace="http://mycompany/passenger"))

然后在Passenger.java中,类本身被注释为:

@XmlElement(name="Passenger", namespace="http://mycompany/passenger")

最后对于Passenger.java中的各个字段,它们的注释是这样的:

@XmlElement(name="TicketNumber", namespace="http://mycompany/passenger")

所以当我有一个看起来像这样的 xml 时:

<train:Train>
   <train:Color>Red</train:Color>
   <train:Passengers>
       <train:Passenger>
           <passenger:TicketNumber>T101</passenger:TicketNumber>
       </train:Passenger>
   </train:Passengers>
</train:Train>

现在我解组收到的这个 xml,并设置了 Train 的 Color 属性和Passenger 的 TicketNumber 属性。但我不知道为什么我需要在 TicketNumber 上的 XmlElement 注释上添加命名空间 url 才能使其工作,但我不需要为 Train 上的 Color 属性这样做。如果我从 TicketNumber 上的 XmlElement 注释中删除命名空间属性,则 xml 中的值不会映射到对象,除非我也从 xml 请求中删除命名空间前缀。我觉得因为我已经在 XmlRootElement 上为乘客定义了命名空间属性,所以我不需要为类中的每个字段都这样做,就像我不需要为火车一样,所以我假设我一定是设置有问题。有人可以指出我正确的方向吗?谢谢!

【问题讨论】:

【参考方案1】:

下面是根据您的模型解释命名空间在JAXB (JSR-222) 中的工作方式。

JAVA 模型

包裹信息

以下是您的 @XmlSchema 注释的修改版本。它包含一些关键信息:

namespace - 默认命名空间,将用于限定未指定其他命名空间的全局元素(对应于 @XmlRootElement@XmlElementDecl 注释(以及基于 elementFormDefault 值的本地元素)的元素。李> elementFormDefault 默认情况下,只有全局元素是命名空间限定的,但通过将值设置为 XmlNsForm.QUALIFIED,所有未指定显式命名空间的元素都将使用 namespace 值限定。 xmlns 是 JAXB 实现应该为这些命名空间使用的首选前缀集(尽管它们可能使用其他前缀)。
@XmlSchema(
    namespace="http://mycompany/train",
    elementFormDefault = XmlNsForm.QUALIFIED,
    xmlns=
       @XmlNs(prefix="train", namespaceURI="http://mycompany/train"), 
       @XmlNs(prefix="passenger", namespaceURI="http://mycompany/passenger")
   
)
package forum15772478;

import javax.xml.bind.annotation.*;

训练

由于Train类对应的所有元素都对应@XmlSchema注解上指定的namespace,所以我们不需要指定任何命名空间信息。

全局元素 - @XmlRootElement 注释对应于全局元素。 本地元素 - @XmlElementWrapper@XmlElement 注释对应于本地元素。
package forum15772478;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement(name="Train")
public class Train 

    private List<Passenger> passengers;

    @XmlElementWrapper(name="Passengers")
    @XmlElement(name="Passenger")
    public List<Passenger> getPassengers() 
        return passengers;
    

    public void setPassengers(List<Passenger> passengers) 
        this.passengers = passengers;
    


乘客

如果与Passenger 类上的属性对应的所有元素都将在http://mycompany/passenger 命名空间中,那么您可以使用@XmlType 注释覆盖@XmlSchema 注释中的namespace

package forum15772478;

import javax.xml.bind.annotation.*;

@XmlType(namespace="http://mycompany/passenger")
public class Passenger 

    private String ticketNumber;

    @XmlElement(name="TicketNumber")
    public String getTicketNumber() 
        return ticketNumber;
    

    public void setTicketNumber(String ticketNumber) 
        this.ticketNumber = ticketNumber;
    


或者,您可以在属性级别覆盖命名空间。

package forum15772478;

import javax.xml.bind.annotation.*;

public class Passenger 

    private String ticketNumber;

    @XmlElement(
        namespace="http://mycompany/passenger",
        name="TicketNumber")
    public String getTicketNumber() 
        return ticketNumber;
    

    public void setTicketNumber(String ticketNumber) 
        this.ticketNumber = ticketNumber;
    


演示代码

可以运行以下演示代码来证明一切正常:

演示

package forum15772478;

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

public class Demo 

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

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum15772478/input.xml");
        Train train = (Train) unmarshaller.unmarshal(xml);

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


input.xml/Output

在下面的 XML 中,我添加了您问题的 XML 文档中缺少的必要命名空间声明。

<train:Train 
   xmlns:train="http://mycompany/train" 
   xmlns:passenger="http://mycompany/passenger">
   <train:Color>Red</train:Color>
   <train:Passengers>
       <train:Passenger>
           <passenger:TicketNumber>T101</passenger:TicketNumber>
       </train:Passenger>
   </train:Passengers>
</train:Train>

更多信息

http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

【讨论】:

感谢超级彻底的回复。这解决了我的问题,并且像我想象的那样工作。我在每个“子类”中错误地使用了 XmlRootElement,并在该注释中声明了命名空间而不是 XmlType。 @Blaise Doughan 请建议我是否可以为两个命名空间使用相同的不带前缀的名称。

以上是关于Jaxb:在同一个包中解组具有多个命名空间的 xml的主要内容,如果未能解决你的问题,请参考以下文章

JAXB:如何在没有命名空间的情况下解组 XML

如果命名空间声明在 SOAP 信封上,如何使用 JAXB 解组 SOAP 响应?

使用命名空间和前缀的 JAXB 解组

JAXB使用多命名空间解组

如何在 jax-ws 网络服务中解组 xml 文件

部分 JSON 在 Go 中解组为地图