Сериализация с помощью JAXB и Any

У меня есть схема, которая определяет следующий тип:

<xsd:complexType name="Payload">
   <xsd:sequence>
      <xsd:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
   </xsd:sequence>
</xsd:complexType>

И это создает такой объект:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Payload", propOrder = {
    "any"
})
public class Payload {

    @XmlAnyElement(lax = true)
    protected List<Object> any;
}

Теперь я пытаюсь добавить другой сгенерированный объект JAXB к этой полезной нагрузке, делая что-то вроде этого:

Class payloadClass = ...;
JAXBContext context = JAXBContext.newInstance( WrapperRequest.class, payloadClass);
...
marshaller.marshal( wrappedRequest );

Но я получаю ужасное исключение, похоже, что он никогда не будет работать, поэтому я решил сначала сериализовать объект полезной нагрузки в XML, а затем добавить его как строку в полезной нагрузке.

StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance( sdoRequest.getClass() );
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(new JAXBElement(new QName("uri", sdoRequest.getClass().getSimpleName()), sdoRequest.getClass(), sdoRequest), writer);
payload.getAny().add( writer.toString() );

И это взрывается с исключением, говоря: "java.lang.String" не содержит @XmlRootElement.

Итак, как будет использоваться xs: когда-либо работайте с JAXB? Кажется, что ничего не работает, потому что JAXB превращает полезную нагрузку в объект и не будет сериализовать что-либо в объекте. Это все внутри Axis2, так что было очень сложно добраться до этого момента.

Ответ 1

Ниже я продемонстрирую JAXB (JSR-222) и any с помощью примера:

Payload

Свойство any аннотируется с помощью @XmlAnyElement(lax=true). Это означает, что для этого свойства, если элемент связан с классом через @XmlRootElement или @XmlElementDecl, тогда экземпляр соответствующего объекта будет использоваться для заполнения свойства, если элемент не будет установлен как экземпляр org.w3c.dom.Element.

package forum13941747;

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Payload", propOrder = {
    "any"
})
public class Payload {

    @XmlAnyElement(lax = true)
    protected List<Object> any;

}

Foo

Ниже приведен пример класса, аннотированного с помощью @XmlRootElement.

package forum13941747;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Foo {

}

Bar

Ниже приведен пример класса без аннотации @XmlRootElement. В этом случае мы будем использовать аннотацию @XmlElementDecl для класса factory (обычно называемого ObjectFactory), аннотированного с помощью @XmlRegistry.

package forum13941747;

public class Bar {

}

ObjectFactory

Ниже приведен пример аннотации @XmlElementDecl для класса Bar.

package forum13941747;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {

    @XmlElementDecl(name="bar")
    public JAXBElement<Bar> createBar(Bar bar) {
        return new JAXBElement<Bar>(new QName("bar"), Bar.class, bar);
    }

}

Input.xml

Ниже приведен входной документ, который мы будем использовать для этого примера. Есть три элемента, соответствующие свойству any. Первый соответствует аннотации @XmlRootElement в классе Foo. Второй соответствует аннотации @XmlElementDecl для класса Bar, а третий не соответствует ни одному из классов домена.

<?xml version="1.0" encoding="UTF-8"?>
<payload>
    <foo/>
    <bar/>
    <other/>
</payload>

Demo

В приведенном ниже демо-коде мы будем развязывать входной документ, затем выводим классы объектов в полученном в результате свойстве any и затем маршалируем объект payload обратно в XML.

package forum13941747;

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

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Payload.class, Foo.class, ObjectFactory.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum13941747/input.xml");
        Payload payload = (Payload) unmarshaller.unmarshal(xml);

        for(Object o : payload.any) {
            System.out.println(o.getClass());
        }

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

}

Выход

Ниже приведен результат запуска демонстрационного кода. Обратите внимание на классы, соответствующие объектам в свойстве any. Элемент Foo стал экземпляром класса Foo. Элемент Bar стал экземпляром JAXBElement, который содержит экземпляр Bar. Элемент other стал экземпляром org.w3c.dom.Element.

class forum13941747.Foo
class javax.xml.bind.JAXBElement
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<payload>
    <foo/>
    <bar/>
    <other/>
</payload>

Ответ 2

Использовать Object Factory для mashelling объекта, подобного ниже, вам не нужно иметь @XmlRootElement в DemoType.java.,

DemoType demoServiceRequest = new DemoType();
ObjectFactory obDemo = new ObjectFactory();  
Request requestObject = new Request();     
requestObject.setAny(obDemo.createDemo(demoServiceRequest));

И добавьте класс DemoType в Request.java, например @XmlSeeAlso ({DemoType.class})

Ответ 3

Если ваша полезная нагрузка представляет собой строку XML, мне удалось решить ту же проблему, используя следующий код:

import javax.xml.parsers.DocumentBuilderFactory;

//...

String XMLPAYLOAD = "...";

Payload payload = new ObjectFactory().createPayload();
try {
    payload.setAny(DocumentBuilderFactory
           .newInstance()
           .newDocumentBuilder()
           .parse(new InputSource(new StringReader(XMLPAYLOAD)))
           .getDocumentElement());
} catch (Exception e) {
    e.printStackTrace();
}

//...