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

2022-01-13 00:00:00 xml annotations namespaces java jaxb

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

I'm new to using namespaces in xml so I am kind of confused and would like some clarification. I have a java service where I am receiving xml documents with many different namespaces and while i got it working, I feel like I must have done something wrong so I want to check. In my package-info.java I have my schema annotation such as:

@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:

I have a Train.java annotated on the class level with:

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

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

and each field in the class annotated with:

@XmlElement(name="Color") for example

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

Train contains a List of Passenger(s) so there's a property

private Set<Passenger> passengers;

并且这个集合带有注释:

and this collection is annotated with:

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

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

Then within Passenger.java the class itself is annotated with:

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

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

Finally for individual fields within Passenger.java, they are annotated like this:

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

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

So when I have an xml that looks like:

<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 上为乘客定义了命名空间属性,所以我不需要为类中的每个字段都这样做,就像我不需要为火车一样,所以我假设我一定是设置有问题.有人可以指出我正确的方向吗?谢谢!

Now I unmarshal this xml I received and Train's Color property is set and Passenger's TicketNumber property is set. But I don't know why I need to add the namespace url on the XmlElement annotation on TicketNumber for that to work but I didn't need to do so for the Color property on Train. If I remove the namespace attribute from the XmlElement annotation on TicketNumber, the value from the xml wont get mapped to the object unless I also remove the namespace prefix from the xml request. I feel like since I've got the namespace attribute defined on the XmlRootElement for Passenger, I shouldn't need to do that for every single field in the class as well just like I didn't have to for Train so I am assuming I must have setup something wrong. Can someone point me in the right direction? Thanks!

推荐答案

下面是JAXB (JSR-222) 基于您的模型.

Below is an explanation of how namespaces work in JAXB (JSR-222) based on your model.

JAVA 模型

包裹信息

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

Below is a modified version of your @XmlSchema annotation. It contains some key information:

  • namespace - 将用于限定全局元素(对应于 @XmlRootElement@XmlElementDecl 注释(以及本地基于 elementFormDefault 值的元素)没有指定另一个命名空间.
  • elementFormDefault 默认情况下,只有全局元素是命名空间限定的,但是通过将值设置为 XmlNsForm.QUALIFIED 所有没有指定显式命名空间的元素都将使用 命名空间值.
  • xmlns 是 JAXB 实现应该为这些命名空间使用的首选前缀集(尽管它们可能使用其他前缀).
  • namespace - The default namespace that will be used to qualify global elements (those corresponding to @XmlRootElement and @XmlElementDecl annotations (and local elements based on the elementFormDefault value) that don't have another namespace specified.
  • elementFormDefault by default only global elements are namespace qualified but by setting the value to be XmlNsForm.QUALIFIED all elements without an explicit namespace specified will be qualified with the namespace value.
  • xmlns is the preferred set of prefixes that a JAXB impl should use for those namespaces (although they may use other prefixes).
@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,所以我们不需要指定任何命名空间信息.

Since all the elements corresponding to the Train class correspond to the namespace specified on the @XmlSchema annotation, we don't need to specify any namespace info.

  • 全局元素 - @XmlRootElement 注释对应于全局元素.
  • 本地元素 - @XmlElementWrapper@XmlElement 注释对应于本地元素.
  • Global Elements - The @XmlRootElement annotation corresponds to a global element.
  • Local Elements - The @XmlElementWrapper and @XmlElement annotations correspond to local elements.
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.

If all the elements corresponding to properties on the Passenger class will be in the http://mycompany/passenger namespace, then you can use the @XmlType annotation to override the namespace from the @XmlSchema annotation.

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;
    }

}

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

Alternatively you can override the namespace at the property level.

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;
    }

}

演示代码

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

The following demo code can be run to prove that everything works:

演示

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 文档中缺少的必要命名空间声明.

In the XML below I have added the necessary namespace declarations that were missing from the XML document in your question.

<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

相关文章