Skip to content

XML collection duplicated element names #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
peregin55 opened this issue Mar 14, 2016 · 7 comments
Open

XML collection duplicated element names #242

peregin55 opened this issue Mar 14, 2016 · 7 comments

Comments

@peregin55
Copy link

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!

@cowtowncoder
Copy link
Member

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

@peregin55
Copy link
Author

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] FasterXML/jackson-dataformat-xml#149

@cowtowncoder
Copy link
Member

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

@Vistritium
Copy link

+1 have this issue with scala List

@cowtowncoder
Copy link
Member

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.

@ConradFinkelstein
Copy link

+1 have this issue with scala List

@afish
Copy link

afish commented May 20, 2024

I had the same issue. I fixed it with adding new constructor with a different signature than the built-in one, and marking it as @JsonCreator. I also had to change annotations so they don't get applied on the constructor parameters.

This is how the code should look like (I didn't verify it with the case from this issue, so there may be typos):

case class Header(
	@(JacksonXmlElementWrapper @field @getter)(localName = "authors")
	@(JacksonXmlElementWrapper @field @getter)(localName = "author")
	authors: Seq[Author]) {
   @JsonCreator
   def this() = {
    this(null)
   }
}

See also this one: https://blog.adamfurmanek.pl/2024/05/20/serializing-collections-with-jackson-in-scala-and-renaming-the-nested-element/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants
@cowtowncoder @afish @peregin55 @Vistritium @ConradFinkelstein and others