JAXB:多态并在编组时忽略类作为类型

时间:2017-05-28 10:44:54

标签: java xml xsd jaxb

我正在使用JAXB来编组Java类。我无法弄清楚我必须在以下场景中使用哪个xml注释:

XSD:

...
<xs:complexType name = "Message">
    <xs:sequence>
        <xs:element name = "memberOne"  type = "xs:hexBinary"/>
        <xs:element name = "memberTwo"  type = "xs:unsignedByte"/>
        <xs:choice>
            <xs:element name = "typeOne"        type = "MessageTypeOne"/>
            <xs:element name = "typeTwo"        type = "MessageTypeTwo"/>
            <xs:element name = "typeThree"      type = "MessageTypeThree"/>
        </xs:choice>
    </xs:sequence>
</xs:complexType>
..

MessageTypeOne/Two/Three之前已定义,但对此方案并不重要。

Java类:

Message.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Message", propOrder = {
    "memberOne",
    "memberTwo",
})
public class Message{

    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(HexBinaryAdapter.class)
    @XmlSchemaType(name = "hexBinary")
    protected byte[] memberOne;
    @XmlSchemaType(name = "unsignedByte")
    protected short memberTwo;
    //Which annotations i have to use here?
    protected MessageBody messageBody;

    ...
}

MessageBody.java

//Which annotations i have to use here?
public class MessageBody {
    //Which annotations i have to use here?
    MessageType myChoice;

    ...
}
XSD文件中未提及

MessageBody。编组时必须“忽略”它。只有其类成员myChoice必须编组。

我已经考虑过实现自己的XmlAdapter,但找不到解决问题的方法。

MessageType.java

public abstract class MessageType { ... }

那是基类。

MessageTypeOne.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MessageTypeOne", propOrder = {
    "memberOne",
    "memberTwo",
})
public class MessageTypeOne extends MessageType {
    @XmlSchemaType(name = "unsignedByte")
    protected short memberOne;
    @XmlSchemaType(name = "unsignedByte")
    protected short memberTwo;

    ...
}

MessageTypeTwo/Three与此类似。

我目前可以实现的XML输出示例:

<Message>
    <memberOne>01020304</memberOne>
    <memberTwo>5</memberOne>
    <MessageBody>
        <MessageTypeOne>
            ...
        </MessageTypeOne>
    </MessageBody>
</Message>

所需的XML输出:

<Message>
    <memberOne>01020304</memberOne>
    <memberTwo>5</memberOne>
    <MessageTypeOne>
            ...
    </MessageTypeOne>
</Message>

所以我只想删除<MessageBody> .. </MessageBody>标记。

显然我只想澄清这个问题。实际上代码要复杂得多。

我不允许更改XSD文件,只允许对java类进行微小更改。我无法改变彼此之间的关系。

我的问题:

  • 我必须使用哪些注释来跳过MessageBody类 编组?我怎么能告诉JAXB只对其成员进行编组 myChoice

  • 多态在JAXB中是如何工作的?我需要使用哪个注释 实现这一目标?

  • 这个问题是否可以用JAXB解决?

编辑:添加了XML示例输出

2 个答案:

答案 0 :(得分:1)

  

多态在JAXB中如何工作?我需要使用哪个注释来实现它?

你可以用。实现这种JAXB多态 @XmlElements注释:

@XmlAccessorType(XmlAccessType.FIELD)
public class MessageBody {
    @XmlElements({
        @XmlElement(name = "MessageTypeOne",   type = MessageTypeOne.class),
        @XmlElement(name = "MessageTypeTwo",   type = MessageTypeTwo.class),
        @XmlElement(name = "MessageTypeThree", type = MessageTypeThree.class)
    })
    protected MessageType myChoice;

    ...
}

编辑:

  

我必须使用哪些注释来跳过MessageBody类   编组?我怎样才能告诉JAXB只对其成员myChoice进行编组?

您可以删除MessageBody课程。 而是将myChoice属性直接放入Message类。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Message", propOrder = {
    "memberOne",
    "memberTwo",
    "myChoice"
})
public class Message{

    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(HexBinaryAdapter.class)
    @XmlSchemaType(name = "hexBinary")
    protected byte[] memberOne;

    @XmlSchemaType(name = "unsignedByte")
    protected short memberTwo;

    @XmlElements({
        @XmlElement(name = "MessageTypeOne",   type = MessageTypeOne.class),
        @XmlElement(name = "MessageTypeTwo",   type = MessageTypeTwo.class),
        @XmlElement(name = "MessageTypeThree", type = MessageTypeThree.class)
    })
    protected MessageType myChoice;

    //...
}

答案 1 :(得分:0)

感谢Thomas Fritschs Answer,他提出了基本想法,我能够解决这个问题。我基本上会用他的代码回答我自己的问题。

我通常不得不面对的问题是我不被允许摆脱MessageBody课程。所以我必须在班级messageBodyChoice中创建一个成员Message才能直接访问它。

此时编组工作正常,解组不会。 XSD文件不包含MessageBody的定义,因此JAXB永远不会自动解组它。

为了手动执行此操作,我添加了以下方法:

void afterUnmarshal(Unmarshaller u, Object parent) { ... }

(观看此帖子了解更多详情:How can I have JAXB call a method after it has completed unmarshalling an XML file into an object?

您可以简单地将此方法添加到任何未进行任何其他工作的非编组的类中。 使用这种方法,我可以在解组后初始化messageBody

代码中的解决方案:

<强> Message.java

@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "Message", propOrder = {
    "memberOne",
    "memberTwo",
    "messageBodyChoice"
})
public class Message{

    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(HexBinaryAdapter.class)
    @XmlSchemaType(name = "hexBinary")
    protected byte[] memberOne;

    @XmlSchemaType(name = "unsignedByte")
    protected short memberTwo;

    protected MessageBody messageBody;

    //This member is only needed for marshalling!!!
    @XmlElements({
        @XmlElement(name = "MessageTypeOne",   type = MessageTypeOne.class),
        @XmlElement(name = "MessageTypeTwo",   type = MessageTypeTwo.class),
        @XmlElement(name = "MessageTypeThree", type = MessageTypeThree.class)
    })
    protected MessageType messageBodyChoice;

    //...

    /* 
     * This setter ensures that messageBodyChoice always contains
     * the value of messageBody.getChoice()
     */
    public void setMessageBody(MessageBody messageBody){
        this.messageBody = messageBody;
        if(messageBody != null) 
            this.messageBodyChoice = messageBody.getChoice();
    }

    //...

    void afterUnmarshal(Unmarshaller u, Object parent) { 
        //at this point messageBody is null
        messageBody = new MessageBody();
        messageBody.setChoice(messageBodyChoice);
    }
}

请注意,我已将@XmlAccessorType更改为XmlAccessType.NONE。 有关@XmlAccessorType here

的更多信息

这可能看起来不干净,但这是我可以找到的唯一可能的方法来避免XML输出中的<MessageBody>标记。