使用JAXB防止从XSD生成空的@XmlType注释

时间:2017-03-29 16:13:41

标签: null jaxb unmarshalling xmlstreamreader

让我先谈谈我的问题归结为什么。我正在使用maven-jaxb2-plugin从XSD架构生成一个名为MyServiceResponse的类。生成的MyServiceResponse类包含带有空名称属性的@XmlType注释。

 @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {

    })

我想生成带有填充name属性的MyServiceResponse,或者完全没有@XmlType注释。这可能是在XJB文件中使用自定义绑定还是以其他方式使用?在任何情况下,如果可能的话,我想在生成期间解决 ,因为在每次运行Maven插件时,MyserviceResponse的手动编辑都会被覆盖。

下面是一个Java类,它演示了我的实际问题。该类使用XmlStreamReader和JAXB从消息中的任何元素开始解组XML消息。 'xml'变量没有完全解组。 'car'属性保持null。发生这种情况是因为xmlns="http://www.example.com/type"命名空间被XmlStreamReader复制到Body元素,而不是Car子元素。不知怎的,JAXB无法看到我希望它继续解组Car元素。 (这里有大问号)

我已经知道一些修复,但它们涉及对XSD或MyServiceResponse类的手动更改。

首先,我可以在XSD上设置elementFormDefault="qualified",我从中生成MyServiceResponse类,ObjectFactory和package-info.java。这有效,但它也会导致我的请求XML消息与限定名称空间一起编组,并且我发送消息的服务不接受这一点。此外,它需要更改XSD,我希望尽可能避免。

其次,在Body和Car @XmlType注释上设置名称有效:@XmlType(name = "Body", propOrder = {})@XmlType(name = "Car", propOrder = {})。但它涉及手动编辑MyServiceResponse。

第三,删除@XmlType(name = "", propOrder = {})注释有效,但也涉及手动编辑MyServiceResponse。

这是一个可复制的演示类:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.Reader;
import java.io.StringReader;

public class XmlStreamReaderUnmarshallingTest {

    private static JAXBContext jaxbContext;

    static {
        try {
            jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }

    private static String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<soapenv:Envelope xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" \n" +
            "\txmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \n" +
            "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
            "\t<soapenv:Body>\n" +
            "\t\t<response xmlns=\"http://www.example.com/type\">\n" +
            "\t\t\t<type:serviceResponse xmlns:type=\"http://www.example.com/type\">\n" +
            "\t\t\t\t<Body>\n" +
            "\t\t\t\t\t<Car>\n" +
            "\t\t\t\t\t\t<Brand>Mitsubishi</Brand>\n" +
            "\t\t\t\t\t\t<Color>Red</Color>\n" +
            "\t\t\t\t\t</Car>\n" +
            "\t\t\t\t</Body>\n" +
            "\t\t\t</type:serviceResponse>\n" +
            "\t\t</response>\n" +
            "\t</soapenv:Body>\n" +
            "</soapenv:Envelope>";


    private static String xmlStripped = "<type:serviceResponse xmlns:type=\"http://www.example.com/type\">\n" +
            "\t\t\t\t<Body>\n" +
            "\t\t\t\t\t<Car>\n" +
            "\t\t\t\t\t\t<Brand>Mitsubishi</Brand>\n" +
            "\t\t\t\t\t\t<Color>Red</Color>\n" +
            "\t\t\t\t\t</Car>\n" +
            "\t\t\t\t</Body>\n" +
            "\t\t\t</type:serviceResponse>";


    public static void main(String[] args) throws JAXBException, XMLStreamException {
        readXml(xml, "serviceResponse");
        readXml(xmlStripped, "serviceResponse");
    }

    private static void readXml(String inputXml, String startFromElement) throws JAXBException, XMLStreamException {

        final XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
        final Reader reader = new StringReader(inputXml);
        final XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(reader);
        final XMLStreamReader streamReader = skipToElement(xmlStreamReader, startFromElement);
        final MyServiceResponse serviceResponse = (MyServiceResponse) unmarshal(streamReader);

        if(serviceResponse.getBody().getCar() == null) {
            System.out.println("It didn't work :-(");
        } else {
            System.out.println("It worked");
        }
    }

    private static XMLStreamReader skipToElement(final XMLStreamReader xsr, final String startAtElement) throws XMLStreamException {
        while (startAtElement != null && xsr.hasNext()) {
            xsr.next();
            if (xsr.hasName()) {
                final String name = xsr.getName().getLocalPart();
                if (name.equals(startAtElement)) {
                    return xsr;
                }
            }
        }

        throw new IllegalArgumentException(String.format("Could not find element %s in response", startAtElement));
    }

    private static Object unmarshal(final XMLStreamReader xsr) throws JAXBException {
        final Object entity = unmarshaller(jaxbContext).unmarshal(xsr);
        return (entity instanceof JAXBElement ? ((JAXBElement) entity).getValue() : entity);
    }

    // Create unmarshaller every time
    private static Unmarshaller unmarshaller(JAXBContext context) throws JAXBException {

        return context.createUnmarshaller();

    }
}


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MyServiceResponse", propOrder = {

})
class MyServiceResponse {

    @XmlElement(name = "Body")
    protected MyServiceResponse.Body body;

    /**
     * Gets the value of the body property.
     *
     * @return
     *     possible object is
     *     {@link MyServiceResponse.Body }
     *
     */
    public MyServiceResponse.Body getBody() {
        return body;
    }

    /**
     * Sets the value of the body property.
     *
     * @param value
     *     allowed object is
     *     {@link MyServiceResponse.Body }
     *
     */
    public void setBody(MyServiceResponse.Body value) {
        this.body = value;
    }


    /**
     * <p>Java class for anonymous complex type.
     *
     * <p>The following schema fragment specifies the expected content contained within this class.
     *
     * <pre>
     * &lt;complexType&gt;
     *   &lt;complexContent&gt;
     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
     *       &lt;all&gt;
     *         &lt;element name="Car" minOccurs="0"&gt;
     *           &lt;complexType&gt;
     *             &lt;complexContent&gt;
     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
     *                 &lt;all&gt;
     *                   &lt;element name="Brand" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
     *                   &lt;element name="Color" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
     *                 &lt;/all&gt;
     *               &lt;/restriction&gt;
     *             &lt;/complexContent&gt;
     *           &lt;/complexType&gt;
     *         &lt;/element&gt;
     *       &lt;/all&gt;
     *     &lt;/restriction&gt;
     *   &lt;/complexContent&gt;
     * &lt;/complexType&gt;
     * </pre>
     *
     *
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {

    })
    public static class Body {

        @XmlElement(name = "Car")
        protected MyServiceResponse.Body.Car car;

        /**
         * Gets the value of the car property.
         *
         * @return
         *     possible object is
         *     {@link MyServiceResponse.Body.Car }
         *
         */
        public MyServiceResponse.Body.Car getCar() {
            return car;
        }

        /**
         * Sets the value of the car property.
         *
         * @param value
         *     allowed object is
         *     {@link MyServiceResponse.Body.Car }
         *
         */
        public void setCar(MyServiceResponse.Body.Car value) {
            this.car = value;
        }


        /**
         * <p>Java class for anonymous complex type.
         *
         * <p>The following schema fragment specifies the expected content contained within this class.
         *
         * <pre>
         * &lt;complexType&gt;
         *   &lt;complexContent&gt;
         *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
         *       &lt;all&gt;
         *         &lt;element name="Brand" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
         *         &lt;element name="Color" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
         *       &lt;/all&gt;
         *     &lt;/restriction&gt;
         *   &lt;/complexContent&gt;
         * &lt;/complexType&gt;
         * </pre>
         *
         *
         */
        @XmlAccessorType(XmlAccessType.FIELD)
        @XmlType(name = "", propOrder = {

        })
        public static class Car {

            @XmlElement(name = "Brand")
            protected String brand;
            @XmlElement(name = "Color")
            protected String color;

            /**
             * Gets the value of the brand property.
             *
             * @return
             *     possible object is
             *     {@link String }
             *
             */
            public String getBrand() {
                return brand;
            }

            /**
             * Sets the value of the brand property.
             *
             * @param value
             *     allowed object is
             *     {@link String }
             *
             */
            public void setBrand(String value) {
                this.brand = value;
            }

            /**
             * Gets the value of the color property.
             *
             * @return
             *     possible object is
             *     {@link String }
             *
             */
            public String getColor() {
                return color;
            }

            /**
             * Sets the value of the color property.
             *
             * @param value
             *     allowed object is
             *     {@link String }
             *
             */
            public void setColor(String value) {
                this.color = value;
            }

        }

    }

}


@XmlRegistry
class ObjectFactory {

    private final static QName _ServiceResponse_QNAME = new QName("http://www.example.com/type", "serviceResponse");

    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.example.type
     *
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link MyServiceResponse }
     *
     */
    public MyServiceResponse createMyServiceResponse() {
        return new MyServiceResponse();
    }

    /**
     * Create an instance of {@link MyServiceResponse.Body }
     *
     */
    public MyServiceResponse.Body createMyServiceResponseBody() {
        return new MyServiceResponse.Body();
    }

    /**
     * Create an instance of {@link MyServiceResponse.Body.Car }
     *
     */
    public MyServiceResponse.Body.Car createMyServiceResponseBodyCar() {
        return new MyServiceResponse.Body.Car();
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link MyServiceResponse }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.example.com/type", name = "serviceResponse")
    public JAXBElement<MyServiceResponse> createServiceResponse(MyServiceResponse value) {
        return new JAXBElement<MyServiceResponse>(_ServiceResponse_QNAME, MyServiceResponse.class, null, value);
    }

}

我的XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.com/type"
           targetNamespace="http://www.example.com/type" elementFormDefault="qualified" >
    <xs:element name="serviceResponse" type="MyServiceResponse"/>
    <xs:complexType name="MyServiceResponse">
        <xs:all>
            <xs:element name="Body" minOccurs="0">
                <xs:complexType>
                    <xs:all>
                        <xs:element name="Car" minOccurs="0">
                            <xs:complexType>
                                <xs:all>
                                    <xs:element name="Brand" type="xs:string" minOccurs="0"/>
                                    <xs:element name="Color" type="xs:string" minOccurs="0"/>
                                </xs:all>
                            </xs:complexType>
                        </xs:element>
                    </xs:all>
                </xs:complexType>
            </xs:element>
        </xs:all>
    </xs:complexType>
</xs:schema>

已添加:

这是XmlStreamReader在启动解组时的XML。 JAXB解组ServiceResponse元素,Body,但不是Car。

<type:serviceResponse xmlns:type="http://www.example.com/type"> 
    <Body xmlns="http://www.example.com/type"> 
        <Car> 
            <Brand>Mitsubishi</Brand> 
            <Color>Red</Color> 
        </Car> 
    </Body> 
</type:serviceResponse>

1 个答案:

答案 0 :(得分:2)

@XmlType name&#39}属性为空,因为Car和Body是匿名类型。 (http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/XmlType.html

在全局命名空间中创建complexTypes,并将其用作type的{​​{1}}。 (小片段)

element

或者像这样创建一个JAXB绑定:https://www.ibm.com/developerworks/library/ws-avoid-anonymous-types/#N100BB

转换XSD:

<xs:element name="Body" type="Body" minOccurs="0" />
<xs:complexType name="Body">