jackson-dataformat-xml icon indicating copy to clipboard operation
jackson-dataformat-xml copied to clipboard

A @JsonUnwrapped with a @JacksonXmlElementWrapper(useWrapping=false) on a collection fails reading from XML, but not JSON

Open jmsjr opened this issue 6 years ago • 1 comments

I have originally created this issue in jackson-databind ( https://github.com/FasterXML/jackson-databind/issues/2073 ) ... but realised only now that I should have created the issue here.

Versions Version Used: 2.9.5 Woodstox Core Used: 5.1.0 Note that I also used Lombok bellow, but only for using the Getter and Setter annotations. Replacing them with the usual boiler plate hand-written getterXXX() and setterXXX() yields the same result.

  <dependencies>
  	<dependency>
  		<groupId>org.projectlombok</groupId>
  		<artifactId>lombok</artifactId>
  		<version>1.16.14</version>
  	</dependency>
  	<dependency>
  		<groupId>com.fasterxml.jackson.dataformat</groupId>
  		<artifactId>jackson-dataformat-xml</artifactId>
  		<version>2.9.5</version>
  	</dependency>
  	<dependency>
  		<groupId>com.fasterxml.jackson.datatype</groupId>
  		<artifactId>jackson-datatype-joda</artifactId>
  		<version>2.9.5</version>
  	</dependency>
	<dependency>
	    <groupId>com.fasterxml.jackson.datatype</groupId>
	    <artifactId>jackson-datatype-jdk8</artifactId>
            <version>2.9.5</version>
	</dependency>
      <dependency>
          <groupId>com.fasterxml.woodstox</groupId>
          <artifactId>woodstox-core</artifactId>
          <version>5.1.0</version>
      </dependency>
    <dependency>
    	<groupId>junit</groupId>
    	<artifactId>junit</artifactId>
    	<version>4.12</version>
    	<scope>test</scope>
    </dependency>
  </dependencies>

Summary: It seems that when you have a collection ( e.g. List ) annotated with JacksonXmlElementWrapper(useWrapping=false) in a bean which itself was annotated with JsonUnwrapped, unmarshalled / reading from XML fails with an exception.

Note that it is NOT an issue when reading from JSON, only from XML.

Example Take the following example ( imports removed for brevity ):

@XmlRootElement
@Getter @Setter
public class Request {
	
	@XmlElement
	@JsonUnwrapped
	private Composite composite = new Composite();
	
}


@XmlType
@Getter @Setter
public class Composite {
	
	@XmlElement
	private String messageId;
	
	@XmlElement
	private Integer number;
	
	@JacksonXmlElementWrapper(useWrapping=false)
	@XmlElement
	private List<Header> headers = new ArrayList<>();

}

@XmlType
@Getter @Setter
public class Header {
	
	@XmlElement
	private String headerId;

}

Unit Test

public class RequestTest {
	

	@Test
	public void testXmlMarshallingAndUnmarshalling() throws Exception {
		final ObjectMapper mapper = new XmlMapper();
		testMarshallingAndUnmarshalling(mapper);
	}

	@Test
	public void testJsonMarshallingAndUnmarshalling() throws Exception {
		final ObjectMapper mapper = new ObjectMapper();
		testMarshallingAndUnmarshalling(mapper);
	}

	private void testMarshallingAndUnmarshalling(ObjectMapper mapper) throws Exception {
		mapper.enable(SerializationFeature.INDENT_OUTPUT);

		final Request request = new Request();
		request.getComposite().setMessageId("ABC");
		request.getComposite().setNumber(123);
		
		final Header header1 = new Header();
		header1.setHeaderId("headerID1");
		final Header header2 = new Header();
		header2.setHeaderId("headerID2");
		
		request.getComposite().getHeaders().add(header1);
		request.getComposite().getHeaders().add(header2);
		
		
		String xmlString = mapper.writeValueAsString(request);
		System.out.println(xmlString);
		
		final Request anotherRequest = mapper.readValue(xmlString, Request.class);
		
		Assert.assertEquals(request.getComposite().getMessageId(), anotherRequest.getComposite().getMessageId());
		Assert.assertEquals(request.getComposite().getNumber(), anotherRequest.getComposite().getNumber());
		
		Assert.assertEquals("ABC", anotherRequest.getComposite().getMessageId());
		Assert.assertEquals(new Integer(123), anotherRequest.getComposite().getNumber());
		Assert.assertEquals(2, anotherRequest.getComposite().getHeaders().size());
		Assert.assertEquals("headerID1", anotherRequest.getComposite().getHeaders().get(0).getHeaderId());
		Assert.assertEquals("headerID2", anotherRequest.getComposite().getHeaders().get(1).getHeaderId());
		
	}

}

... which writes to and reads from XML and JSON.

  • Writing to JSON works. Reading the JSON that was just written also works.
  • Writing to XML works, Reading from XML that was just written fails.

It seems to think that the property from the unwrapped Composite should be a property on the Request.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running RequestTest
{
  "messageId" : "ABC",
  "number" : 123,
  "headers" : [ {
    "headerId" : "headerID1"
  }, {
    "headerId" : "headerID2"
  } ]
}
<Request>
  <messageId>ABC</messageId>
  <number>123</number>
  <headers>
    <headerId>headerID1</headerId>
  </headers>
  <headers>
    <headerId>headerID2</headerId>
  </headers>
</Request>

Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.475 sec <<< FAILURE!
testXmlMarshallingAndUnmarshalling(RequestTest)  Time elapsed: 0.13 sec  <<< ERROR!
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "messageId" (class Request), not marked as ignorable (one known property: "composite"])
 at [Source: (StringReader); line: 2, column: 29] (through reference chain: Request["messageId"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:822)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1152)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1582)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1560)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
	at RequestTest.testMarshallingAndUnmarshalling(RequestTest.java:44)
	at RequestTest.testXmlMarshallingAndUnmarshalling(RequestTest.java:16)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)


Results :

Tests in error: 
  testXmlMarshallingAndUnmarshalling(RequestTest): Unrecognized field "messageId" (class Request), not marked as ignorable (one known property: "composite"])(..)

Tests run: 2, Failures: 0, Errors: 1, Skipped: 0

jmsjr avatar Jun 27 '18 11:06 jmsjr

+1

probepark avatar Jan 16 '19 10:01 probepark