从 XmlStreamReader 解组 JAXB 导致空对象

JAXB unmarshalling from XmlStreamReader results in null object

我创建了一个 Java 程序来解组 XML 消息,其中可以提供 "startTag" 以在消息中的特定元素处开始解组。我使用 XMLStreamReader 来进行选择。

下面是我 运行 遇到的问题的完整复制粘贴演示。 'xml' 消息是我需要处理的。不幸的是,它在结果中产生了一个 null 汽车对象,而 'xmlStripped' 消息产生了一个完全未编组的结果。

我将问题隔离到 <response> 元素中的 xmlns="http://www.example.com/type" 命名空间。当我删除它时,'xml' 被正确解组。

无法控制 XML。 'xml' 变量是我需要使用的。我对 XSD/ObjectFactory 很少 控制,所以我的第一个行动方案是在解组过程中寻找解决方案。

如果您知道失败的原因以及您是否有解决方案,请告诉我。谢谢!

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

}

您可以为您的模型设置 elementFormDefault="qualified",方法是创建一个包-info.java 并添加:

包-info.java

@XmlSchema(namespace="http://www.example.com/type", elementFormDefault=XmlNsForm.QUALIFIED)

package mypkg;

import javax.xml.bind.annotation.*;

这将使您的常规 XML 正常工作,但不会让您的 "stripped" XML 正常工作。两者之间的区别,除了外部包装元素的 "stripping" 之外,您的原始 XML 具有默认名称空间集,而剥离的 XML 没有。

在@XmlType 注释上设置名称属性:

@XmlType(name = "Body", propOrder = {

})
public static class Body { ... }

@XmlType(name = "Car", propOrder = {

})
public static class Car { ... }