当解组时,目标Java对象中的MOXy字段顺序很重要

时间:2015-05-25 08:37:40

标签: java jaxb moxy xmlanyelement

似乎MOXy中有一个错误。当Request类中的字段声明为metaInfo然后是content时,下面的代码片段工作得很好,但是当以相反的顺序(first first和metaInfo second)声明字段时,测试在解组时失败,异常。

The exception thrown is:
Going with type: APPLICATION_XML
Original request = {content=Payload = {[one, two, three]}, metaInfo=requestMetaInfo = {confirmation=false}}
Marshaled as application/xml: <?xml version="1.0" encoding="UTF-8"?><request><collection><collection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">one</collection><collection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">two</collection><collection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">three</collection></collection><metaInfo><confirmation>false</confirmation></metaInfo></request>
Local Exception Stack: 
Exception [EclipseLink-32] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to set value [[one, two, three]] for instance variable [collection] of type [java.util.Collection] in the object.  The specified object is not an instance of the class or interface declaring the underlying field, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: Can not set java.util.Collection field test2.TestCase2$Payload.collection to test2.TestCase2$RequestMetaInfo
Mapping: org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping[collection]
Descriptor: XMLDescriptor(test2.TestCase2$Payload --> [DatabaseTable(collection)])
    at org.eclipse.persistence.exceptions.DescriptorException.illegalArgumentWhileSettingValueThruInstanceVariableAccessor(DescriptorException.java:703)
    at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.setAttributeValueInObject(InstanceVariableAttributeAccessor.java:188)
    at org.eclipse.persistence.mappings.DatabaseMapping.setAttributeValueInObject(DatabaseMapping.java:1652)
    at org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping.setAttributeValueInObject(XMLCompositeCollectionMapping.java:741)
    at org.eclipse.persistence.internal.oxm.XMLCompositeCollectionMappingNodeValue.setContainerInstance(XMLCompositeCollectionMappingNodeValue.java:265)
    at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endDocument(UnmarshalRecordImpl.java:628)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endDocument(AbstractSAXParser.java:745)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:515)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:648)
    at org.eclipse.persistence.internal.oxm.record.XMLReader.parse(XMLReader.java:243)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:978)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:425)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:635)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:706)
    at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:643)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:339)
    at test2.TestCase2.main(TestCase2.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.IllegalArgumentException: Can not set java.util.Collection field test2.TestCase2$Payload.collection to test2.TestCase2$RequestMetaInfo
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
    at java.lang.reflect.Field.set(Field.java:741)
    at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.setAttributeValueInObject(InstanceVariableAttributeAccessor.java:141)
    ... 24 more

这是重现问题的测试。

package test2;

import org.eclipse.persistence.jaxb.JAXBContext;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;
import org.eclipse.persistence.oxm.MediaType;

import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * Test that fails if Request.content field declared before than Request.metaInfo, but works well if
 * Request.metaInfo goes first in declaration
 *
 * MOXy 2.6.0
 */
public class TestCase2 {
    @XmlRootElement(name = "request")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Request<T> {
        @XmlAnyElement(lax = true)
        private T content; // NB!: Causes test failure if declared before metaInfo, if declaration order is changed, things work

        @XmlElement
        private RequestMetaInfo metaInfo;

        public Request() {
        }

        public Request(T content, RequestMetaInfo metaInfo) {
            this.content = content;
            this.metaInfo = metaInfo;
        }

        @Override
        public String toString() {
            return "request = {" + "content=" + content + ", metaInfo=" + metaInfo +'}';
        }

    }
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class RequestMetaInfo {
        @XmlElement
        private Boolean confirmation = false;
        @Override
        public String toString() {
            return "requestMetaInfo = {" + "confirmation=" + confirmation +'}';
        }
    }

    @XmlRootElement(name = "collection")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Payload<T> {
        @XmlElement
        private Collection collection = new ArrayList();

        public Payload(){}
        public Payload(Collection<T> collection){
            this.collection.addAll(collection);
        }

        public Collection<T> getCollection() {
            return collection;
        }

        @Override
        public String toString() {
            return "Payload = {" + getCollection()+"}";
        }
    }

    // Element name holding value of primitive types
    public static final String VALUE_ELEMENT = "value";
    // Attribute prefix in JSON
    public static final String ATTRIBUTE_PREFIX = "@";
    public static void main(String... args) {
        try {
            for (MediaType type : new MediaType[]{MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) {
                System.out.println("Going with type: " + type);
                JAXBContext context = (JAXBContext) JAXBContextFactory.createContext(
                    new Class[]{
                        Request.class,
                        RequestMetaInfo.class,
                        Payload.class
                    },
                    Collections.emptyMap());

                Marshaller marshaller = context.createMarshaller();
                marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, type);
                marshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
                marshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, ATTRIBUTE_PREFIX);
                marshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, VALUE_ELEMENT);
                marshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);

                Request original = new Request(
                    new Payload(Arrays.asList("one","two","three")),
                    new RequestMetaInfo()
                );
                System.out.println("Original " + original.toString());

                StreamResult result = new StreamResult(new StringWriter());
                marshaller.marshal(original, result);
                String generated = result.getWriter().toString();
                System.out.println("Marshaled as " + type.getMediaType() + ": " + generated);

                Unmarshaller unmarshaller = context.createUnmarshaller();
                unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, type);
                unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
                unmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, ATTRIBUTE_PREFIX);
                unmarshaller.setProperty(UnmarshallerProperties.JSON_VALUE_WRAPPER, VALUE_ELEMENT);
                unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);


                Request unmarshalled = unmarshaller.unmarshal(new StreamSource(new StringReader(generated)), Request.class).getValue();
                System.out.println("Unmarshaled " + unmarshalled.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

请问任何想法可能出错?

2 个答案:

答案 0 :(得分:3)

经过一些调试后我发现,这个bug可能出现在org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endDocument()中。

在Payload.collection被解组后,集合的populateContainerValues没有被取消。然后,当moxy unmarshalls metaInfo元素时,它会尝试处理它,因为它是Payload.collection,将集合分配给Request.metaInfo,这会导致异常。

我做了丑陋的解决方法(因为我无法解决)并且只是在Request对象中更改了字段声明的顺序,但我相信它将在MOXy中修复一天。

更新: 我向MOXy bugzilla提交了一个错误:https://bugs.eclipse.org/bugs/show_bug.cgi?id=468337

答案 1 :(得分:0)

我遇到了同样的问题。 我的解决方案:

@XmlMixed
@XmlAnyElement(lax = true)
private String content;

享受;)