Jaxb:在同一包中使用多个命名空间取消编组 xml

2022-09-02 20:49:05

我是在xml中使用命名空间的新手,所以我有点困惑,希望得到一些澄清。我有一个java服务,我正在接收具有许多不同命名空间的xml文档,当我让它工作时,我觉得我一定做错了什么,所以我想检查。在我的包信息中.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
)

我有一个火车.java在班级层面上标注:

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

以及类中标注以下内容的每个字段:

@XmlElement(name="Color") for example

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

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's TicketNumber 属性。但是我不知道为什么我需要在TicketNumber上的XmlElement注释上添加命名空间URL才能工作,但我不需要为Train上的Color属性执行此操作。如果我从 TicketNumber 上的 XmlElement 注释中删除命名空间属性,则 xml 中的值不会映射到对象,除非我还从 xml 请求中删除命名空间前缀。我觉得既然我已经在XmlRootElement for Passenger上定义了命名空间属性,我就不需要为类中的每个字段执行此操作,就像我不必为Train所做的那样,所以我假设我一定设置了错误的东西。有人能给我指出正确的方向吗?谢谢!


答案 1

下面根据模型对命名空间在 JAXB (JSR-222) 中的工作方式进行了说明。

JAVA 模型

软件包信息

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

  • namespace- 将用于限定未指定其他命名空间的全局元素(对应于和批注(以及基于值的本地元素))的默认命名空间。@XmlRootElement@XmlElementDeclelementFormDefault
  • elementFormDefault默认情况下,只有全局元素是命名空间限定的,但是通过将值设置为所有未指定显式命名空间的元素,将使用该值进行限定。XmlNsForm.QUALIFIEDnamespace
  • xmlns是 JAXB impl 应该用于这些命名空间的首选前缀集(尽管它们可能使用其他前缀)。
@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.*;

火车

由于与类对应的所有元素都对应于注释上指定的元素,因此我们不需要指定任何命名空间信息。Trainnamespace@XmlSchema

  • 全局元素 - 注释对应于全局元素。@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;
    }

}

乘客

如果与类上的属性对应的所有元素都将位于命名空间中,则可以使用该批注来覆盖 from 批注。Passengerhttp://mycompany/passenger@XmlTypenamespace@XmlSchema

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

}

输入.xml/输出

在下面的 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>

详细信息


答案 2

推荐