5

I am parsing the following external XML with Jackson.

<SomeRootObject>
  <Events>
    <Event>
      <EventID>248739296</EventID>
      ...
      <Event>1709</Event>
      ...

I defined a POJO for “Event”.

@JacksonXmlRootElement(localName = "Event")
public class MyEvent {
    @JsonProperty("EventID")
    public String eventID;

    ...

    @JsonProperty("Event")
    public int event;

    ...

As you can see one of the fields in this POJO is also mapped as “Event”. And so Jackson complains that it can’t create an int from an event:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of int out of START_OBJECT token
at [Source: java.io.StringReader@12417468; line: 1, column: 280] (through reference chain: be.parkd.api.tnt.ram.model.RamEvents[“Event”]->java.util.ArrayList[0]->be.parkd.api.tnt.ram.model.RamEvent[“Event”]).

Can this case be handled with Jackson?

One dirty fix I have in mind is to preprocess the XML to change the underlying Event but I would prefer a cleaner solution.

Michiel Ariens
  • 288
  • 4
  • 12

1 Answers1

4

Variant 1

The following example reads a list of <Event> elements wrapped in an <Events> element. The <Event> itself contains another nested <Event> element. This seems to be not a problem for Jackson.

Note: I used TypeReference<List<Event>>() {} as a serialization rule.

@Test
public void test1() throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new XmlMapper();
    List<Event> event=mapper.readValue("<Events><Event><EventID>248739296</EventID><Event>1709</Event></Event><Event><EventID>248739297</EventID><Event>1710</Event></Event></Events>", new TypeReference<List<Event>>() {
    });
    System.out.println(toString(event));
}

public String toString(Object obj) {
    try {
        StringWriter w = new StringWriter();
        new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true).writeValue(w, obj);
        return w.toString();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

With Event.java

@JacksonXmlRootElement(localName = "Event")
public class Event {
    @JsonProperty("EventID")
    private String eventID;
    @JsonProperty("Event")
    private int event;
    public String getEventID() {
        return eventID;
    }
    public void setEventID(String eventID) {
        this.eventID = eventID;
    }
    public int getEvent() {
        return event;
    }
    public void setEvent(int event) {
        this.event = event;
    }
}

Prints

[ {
  "EventID" : "248739296",
  "Event" : 1709
}, {
  "EventID" : "248739297",
  "Event" : 1710
 } ]

So, it works!

Variant 2

@Test
public void test2() throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new XmlMapper();
    SomeRootObject object=mapper.readValue("<SomeRootObject><Events><Event><EventID>248739296</EventID><Event>1709</Event></Event><Event><EventID>248739297</EventID><Event>1710</Event></Event></Events></SomeRootObject>", SomeRootObject.class);
    System.out.println(toString(object));
}

With SomeRootObject.class

@JacksonXmlRootElement(localName = "SomeRootObject")
public class SomeRootObject {
    @JsonProperty("Events")
    List<Event> events;
    public SomeRootObject() {

    }
    public List<Event> getEvents() {
        return events;
    }
    public void setEvents(List<Event> events) {
        this.events = events;
    }
}

Prints

{
  "Events" : [ {
    "EventID" : "248739296",
    "Event" : 1709
  }, {
    "EventID" : "248739297",
    "Event" : 1710
  } ]
}

Works too!

jschnasse
  • 8,526
  • 6
  • 32
  • 72
  • Thank you. I will test that I have the same behaviour. I should add though that i don't call `mapper.readValue(..., Event.class)`. I call the mapper with a higher XML object's class. – Michiel Ariens Oct 16 '18 at 13:09
  • After testing I can confirm the example you provide does work. However wrapping the XML input with `...` fails. I believe Jackson reads the `` tag and infers it must deserialise another Event object. – Michiel Ariens Oct 16 '18 at 15:48
  • Not sure, if we are on the right track. Look at my editing. I added `` – jschnasse Oct 16 '18 at 16:07
  • Indeed this case works. When changing `new TypeReference>()` to `SomeRootObject.class` that's when it fails. There are POJOs for SomeRootObject, Events and Event. – Michiel Ariens Oct 17 '18 at 08:28
  • 1
    However I am able to bypass the problem by rewriting the code to match your example. I just wonder what would happen in case this woud have happened in a deeper nested structure. – Michiel Ariens Oct 17 '18 at 08:35
  • If you are still wondering - it just happened to me and I can't do anything about it. Thought I'm gonna find some solution and I found this and lost all hope :) – Shadov Jun 19 '19 at 14:41
  • Hi @shadov, I added a second example (Variant 2). It seems to work as well. Maybe you missed some annotations in your classes?! – jschnasse Jun 20 '19 at 08:45
  • @jschnasse Do you know what Jackson actually does with ? Are there any naming constraints? Your code does not mention it in the first variant. What if I just name it ? Rephrased: Does `JacksonXmlRootElement` make Jackson simply ignore the top-level element? – Jonathan Komar Nov 13 '19 at 06:45
  • Yes, and No. In the first variant I explicitly tell Jackson to deserialize the xml as a list ( scroll to the end of the line). The only thing it does with is to handle it's content as a list. So yes, the name of the root element is ignored in that case. The `JacksonXmlRootElement` is used to deserialize each single element in the list. Those elements must be named . In the second variant I added an additional class `SomeRootObject`. – jschnasse Nov 13 '19 at 07:58