Я использую 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;

    ...
}

MessageBody не упоминается в файле XSD. Это нужно "игнорировать" при сортировке. Только его член класса 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

1
ysfaran 28 Май 2017 в 13:44

2 ответа

Лучший ответ

Благодаря ответу Томаса Фрича, который придумал основную идею, я смог решить эту проблему. Я в основном буду использовать его код для ответа на свой вопрос.

Проблема, с которой я обычно сталкивался, заключалась в том, что мне не разрешили избавиться от класса MessageBody. Поэтому мне пришлось создать член messageBodyChoice в классе Message, чтобы иметь к нему прямой доступ.

Маршаллинг отлично работает, а не маршаллинг - нет. Файл XSD не содержит определения для MessageBody, поэтому JAXB никогда не сможет его автоматически разархивировать.

Чтобы сделать это вручную, я добавил следующий метод:

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

(Смотрите этот пост для более подробной информации: Как я могу заставить JAXB вызывать метод после того, как он завершил демаршализацию файла XML в объект?)

Вы можете просто добавить этот метод к любому классу, который будет распакован без каких-либо других усилий. с помощью этого метода я смог инициализировать 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 здесь.

Это может показаться нечистым, но это единственный возможный способ избежать тега <MessageBody> в выводе XML.

0
ysfaran 29 Май 2017 в 08:50

Как работает полиморфизм в 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
Thomas Fritsch 28 Май 2017 в 18:25