1

I have a class Box which has a field elements which is of type Elements that implements Iterable (see the code below). The latter contains a List which is also called elements

If I set the field elements of Elements to be private, then the serialization would treat it as an array rather than a POJO

Serialization done by Version A

{
  "elements" : [ "a", "b", "c" ]
}

However, I could not deserialize this string. The error was:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `ser_deser_test.Elements` out of START_ARRAY token
 at [Source: (String)"{
  "elements" : [ "a", "b", "c" ]
}"; line: 2, column: 16] (through reference chain: 

If I, instead, set elements to be public, then elements of Box would be treated as a POJO, and I had this funny two levels of elements in it. The deserialization worked in this case, however.

Serialization by Version B

{
  "elements" : {
    "elements" : [ "a", "b", "c" ]
  }
}

My question is: how can I get deserializatio to work for Version A?

Code

Box.java

package ser_deser_test;

public class Box {
    public Box() {
        super();
    }

    public Elements elements;

    public Box(Elements elements) {
        super();
        this.elements = elements;
    }

}

Elements.java

package ser_deser_test;

import java.util.Iterator;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonFormat;

public class Elements implements Iterable<String> {
    // private List<String> elements; // Version A
    // public List<String> elements;  // Version B

    public Elements() {
        super();
    }

    public Elements(List<String> elements) {
        super();
        this.elements = elements;
    }

    @Override
    public Iterator<String> iterator() {
        return elements.iterator();
    }

}

TestSerDeser.java

package ser_deser_test;

import java.io.IOException;
import java.util.Arrays;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class TestSerDeser {

    public static void main(String[] args) {
        Elements elements = new Elements(Arrays.asList("a", "b", "c"));
        Box      box = new Box(elements);

        ObjectMapper om = new ObjectMapper();

        om.enable(SerializationFeature.INDENT_OUTPUT);
        try {
            // Serialize
            String s = om.writeValueAsString(box);

            // Deserialize
            Box box2 = om.readValue(s, Box.class);

            boolean dummy = false;

        } catch (JsonProcessingException e2) {
            e2.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }         
    }
}
leeyuiwah
  • 6,562
  • 8
  • 41
  • 71

2 Answers2

2

Why not using simple property of List<String>?

public class Elements {

   public List<String> elements;

Or as @EugenCovaci comment if you don't need specific Object

 public class Box {
   public List<String> elements; //directly into Box class

For keeping private List<String> elements; just add getter to be used as property:

public List<String> getElements() {
   return elements;
}

Also you can add @JsonProperty

  @JsonProperty
  private List<String> elements;
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • Or put `List elements;` directly into `Box` class. –  Nov 20 '18 at 10:47
  • Thanks (to both you and @Engen Covaci) -- Assuming that we need to keep the structure of Version A, is there a way to deserialize properly? – leeyuiwah Nov 20 '18 at 11:04
  • 1
    @leeyuiwah see my updated, add getter `public List getElements() { return elements; }` – Ori Marko Nov 20 '18 at 11:07
  • 1
    @leeyuiwah Just add `om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);` See my answer. A public getter is even better. –  Nov 20 '18 at 11:08
1

Your code works fine with:

package ser_deser_test;

import java.util.Iterator;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonFormat;

public class Elements implements Iterable<String> {
    // private List<String> elements; // Version A
    public List<String> elements;  // Version B

    public Elements() {
        super();
    }

    public Elements(List<String> elements) {
        super();
        this.elements = elements;
    }

    @Override
    public Iterator<String> iterator() {
        return elements.iterator();
    }

}

and fails with Version A decommented because, by default, Jackson doesn't have acces to private fields. To make it work with Version A modify TestSerDeser class like this:

ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

in order to allow Jackson access the private field List<String> elements or, even better, add a public getter for this field.