看来MOXy中有错误。当类Request中的字段先声明为metaInfo然后声明为content时,下面的代码可以完美地工作,但是当声明相反顺序的字段(先声明内容而后声明metaInfo)时,测试无法进行编组,但出现异常。
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();
}
}
}
请任何想法可能是什么错误?
最佳答案
经过一些调试后,我发现该错误可能在org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endDocument()中。
取消编组Payload.collection后,集合populatedContainerValues不会为空。然后,当moxy取消编组metaInfo元素时,它将尝试像Payload.collection一样对其进行处理,将集合分配给Request.metaInfo,这将导致异常。
我做了难看的解决方法(因为我无法修复它),只是更改了Request对象中字段声明的顺序,但是我相信这一天将在MOXy中修复。
更新:
我向MOXy bugzilla提交了错误:https://bugs.eclipse.org/bugs/show_bug.cgi?id=468337