jackson-module-scala
jackson-module-scala copied to clipboard
XML collection duplicated element names
I'm having trouble getting a case class with a collection field to serialize the way I want. I have a data model that looks like this:
@JsonRootName("document")
case class Document(header: Header)
case class Header(authors: Seq[Author])
case class Author(name: String)
And I'm using Jackson to produce an XML:
val doc = Document(Header(Seq(Author("Dick Jones"), Author("Bob Morton"))))
val mapper = new XmlMapper()
mapper.registerModule(DefaultScalaModule)
val xml = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(doc)
The document that looks like this:
<document>
<header>
<authors>
<authors>
<name>Dick Jones</name>
</authors>
<authors>
<name>Bob Morton</name>
</authors>
</authors>
</header>
</document>
But <authors> is duplicated with its nested elements. I want the <authors> tag to contain a list of <author> elements.
I can get this output by adding the following annotations to the Header case class:
case class Header(@JacksonXmlElementWrapper(localName = "authors") @JacksonXmlProperty(localName = "author") authors: Seq[Author])
This serializes the way I want, but when deserializing I get the following exception:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Could not find creator property with name 'authors' (in class sample.Header)
at [Source: java.io.StringReader@3d778e7; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:216)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:894)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:541)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:228)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:143)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:406)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:444)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.findDeserializer(StdDeserializer.java:946)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:446)
at com.fasterxml.jackson.databind.deser.std.DelegatingDeserializer.resolve(DelegatingDeserializer.java:61)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:296)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:477)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3890)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3785)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2779)
at sample.Main$.main(Message.scala:26)
at sample.Main.main(Message.scala)
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:144)
Is there something I can do to get this to deserialize properly?
Thanks for your help!
I think this would belong under jackson-dataformat-xml, unless there is something Scala specific.
In Java this code works if I use the JavaBean pattern to create the domain model. It sounds like JacksonXmlElementWrapper doesn't really work very well with immutable Java objects [1].
In Scala I'd like to avoid JavaBean since case-classes use immutability. Is there a way to control the element names for Seq/immutable lists? Either with JacksonXmlElementWrapper or something else?
Thanks!
[1] https://github.com/FasterXML/jackson-dataformat-xml/issues/149
@peregin55 Perhaps this has more to do with problems with case classes, related to handling of @JsonCreator and constructors -- this is what exception also suggests. So I don't think it is XML-specific after all, but more likely Scala issue. So scratch the suggestion to move this.
However what would be useful would be equivalent json-only example, I think.
+1 have this issue with scala List
Come to think of it, there's a good chance this would be due to XML-specific extra handling that is required. But if anyone can double-check to ensure this can not be reproduced with json, that would be great. Building a combined test is problematic from module perspective; neither module should (ideally) depend on each either wrt releases.
+1 have this issue with scala List