1

I have a JSON object like

{
  "id" : "1",
  "children" : ["2","3"]
}

And I have a Java object like (constructor, getters and setters are omitted):

public class Entity {
    public String id;
    public String children;
}

I want this JSON to be deserialized to my Java object by this code using Jackson:

Entity entity = mapper.readValue(json, Entity.class);

But get the following error:

Can not deserialize instance of java.lang.String out of START_ARRAY token

How can I solve it without changing type of children field?

The children field is expected to have the following value: ["2","3"].

6
  • Could you change the type of children to List<String>? Commented Jan 24, 2017 at 9:50
  • how do you want to store "children":["2","3"] if you dont want to change type ?? you want only first value ? Commented Jan 24, 2017 at 9:53
  • I want something like "["2","3"]" (quotes can be with backslashes) Commented Jan 24, 2017 at 9:55
  • so you need to send json like "children":"[2,3]" , if you can, otherwise you'd need create and register custom mapper Commented Jan 24, 2017 at 9:56
  • 1
    I don't think you can do it with standard mapper, write custom mapper Commented Jan 24, 2017 at 10:00

3 Answers 3

2

Creating a custom deserializer

Create a custom deserializer to get the raw JSON value. You can choose one of the following implementations, according to your needs:

  1. It will give you the JSON as is, that is, keeping all the spaces and tabs:
public class RawJsonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
           throws IOException, JsonProcessingException {

        long begin = jp.getCurrentLocation().getCharOffset();
        jp.skipChildren();
        long end = jp.getCurrentLocation().getCharOffset();

        String json = jp.getCurrentLocation().getSourceRef().toString();
        return json.substring((int) begin - 1, (int) end);
    }
}
  1. It will give you the JSON without extra spaces and tabs:
public class RawJsonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
           throws IOException {

        JsonNode node = jp.getCodec().readTree(jp);
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        return mapper.writeValueAsString(node);
    }
}

Annotate your class to use the deserializer defined above

Change the Entity class by annotating the children attribute with @JsonDeserialize referencing the deserializer defined above:

public class Entity {

    public String id;

    @JsonDeserialize(using = RawJsonDeserializer.class)
    public String children;
}

Parsing the JSON

Then parse the JSON using ObjectMapper and Jackson will use your custom deserializer:

String json = "{\"id\":\"1\",\"children\":[\"2\",\"3\"]}";

ObjectMapper mapper = new ObjectMapper();
Entity entity = mapper.readValue(json, Entity.class);

The value of the children attribute will be ["2","3"].


For more details, have a look at this question.

Sign up to request clarification or add additional context in comments.

Comments

1

Marshall your objects into JSON format.
Then Unmarshall from the JSON file

public interface MarshallingSupport {
public String marshal(Object object);
public <T> T unmarshal(String s, Class<T> t);
}

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JacksonJSONMarshallingSupport implements MarshallingSupport {

private final ObjectMapper mapper;

   public JacksonJSONMarshallingSupport(ObjectMapper mapper) {
       this.mapper = mapper;
       this.mapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
   }

    @Override
    public String marshal(Object object) {
       try {
            return mapper.writeValueAsString(object);
           } catch (JsonProcessingException ex) {
               throw new RuntimeException(ex);
           }
   }

   @Override
   public <T> T unmarshal(String s, Class<T> t) {
       try {
             T newObj = mapper.readValue(s, t);
             return newObj;
           } catch (IOException ex) {
               throw new RuntimeException(ex);
           }
   }
}

Comments

1

Taking the @Cassio's answer and if you don't want to or you can't annotate your Entity class, just add some configurations.

First create an abstract class [for method annotation purpose you can create an interface, but in this case we will annotate a bean property so we create an abstract class, and if you also want to annotate a method in this abstract class you have to declare that method as abstract] that will be like a mime bean for Jackson configurations:

public abstract class EntityMixIn {

    @JsonDeserialize(using = RawJsonDeserializer.class)
    public String children;

}

Now, you have to tell your mapper to take this mixin class and act like the original Entity class just for this configuration purpose:

mapper.addMixIn(Entity.class, EntityMixIn.class);

4 Comments

Jackson mix-in annotation is a great resource when modifying your classes is not an option. However saying that the mix-in interface will behave like the original Entity is not accurate. You can think of mix-in annotations as a kind of aspect-oriented way of adding more annotations during runtime to augment the statically defined ones.
Yeap! you are right, but it is just for the example and that's why previously I said that it will be like a mime bean, but I will update this affirmation. Thank you.
I've upvoted your answer. I think it's a nice contribution to the solution I had provided :)
Thank you @Cassio. I also clarified the abstract class purpose.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.