slim
slim

Reputation: 41271

Jackson: deserialize JSON with XML-like lists

A service I don't control is sending me JSON that looks like this when there is one item:

{ items: {
     item: { ... }
}

... like this when there is no item:

{ items: null }

... and like this when there are two or more items:

 { items: {
    item: [
        { ... }, 
        { ... }
    ]
    }
 }

I believe this is because the service was designed to produce XML like this:

  <items>
       <item>....</item>
       <item>....</item>
  </items>

... and has thrown the results at an XML->JSON convertor without paying much attention to the result.

Using Jackson, I can handle the "sometimes null, sometimes object, sometimes array" issue in Jackson by configuring my ObjectMapper with DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.

I can handle the two-layer structure by defining an Items class with a field List<Item> item.

However, the Items class is only there as a sop to the weird JSON format. Other than by writing a whole custom deserializer, can I persuade Jackson to deserialize to an object as if the structure was:

{ items: [ { ... }, { ... } ] }

?

Upvotes: 0

Views: 481

Answers (2)

StaxMan
StaxMan

Reputation: 116630

Usually the easiest way to handle mapping is to reflect structure of data format in your POJO structure. So in this case, you would add one functionally unnecessary, but mapping-wise necessary, property:

public class POJO {
    public ItemHolder items;

    // might want convenience method for Java access, but not with getter name
    public List<Item> items() {
       return (items == null) ? null : items.item;
    }
} 
public class ItemHolder {
    public List<Item> item;
}

and you do still also need to enable DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to handle mapping from single Item into List<Item>.

But perhaps the other answer of using @JsonUnwrapped also helps; it is another option.

Upvotes: 0

Michał Ziober
Michał Ziober

Reputation: 38720

It looks like you have a root object which contains Items property. In this case you can use @JsonUnwrapped annotation. See below class:

class Root {

    @JsonUnwrapped
    private Items items = new Items();

    public Items getItems() {
        return items;
    }

    public void setItems(Items items) {
        this.items = items;
    }
}

Simple usage:

ObjectMapper mapper = new ObjectMapper();
Items items = new Items();
items.setItem(Arrays.asList(new Item(), new Item()));

Root wrapper = new Root();
wrapper.setItems(items);

String json = mapper.writeValueAsString(wrapper);
System.out.println(json);

Above program prints:

{"item":[{"name":"X"},{"name":"X"}]}

Upvotes: 1

Related Questions