jackson-module-scala icon indicating copy to clipboard operation
jackson-module-scala copied to clipboard

XML collection duplicated element names

Open peregin55 opened this issue 9 years ago • 7 comments

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!

peregin55 avatar Mar 14 '16 20:03 peregin55

I think this would belong under jackson-dataformat-xml, unless there is something Scala specific.

cowtowncoder avatar Mar 14 '16 21:03 cowtowncoder

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 avatar Mar 15 '16 14:03 peregin55

@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.

cowtowncoder avatar Apr 07 '16 03:04 cowtowncoder

+1 have this issue with scala List

Vistritium avatar Jan 12 '17 11:01 Vistritium

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.

cowtowncoder avatar Jan 16 '17 18:01 cowtowncoder

+1 have this issue with scala List

ConradFinkelstein avatar Oct 18 '17 14:10 ConradFinkelstein