How to serialize JSON with array field to object with String field?

北城以北 提交于 2021-02-10 14:16:42

问题


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"].


回答1:


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.




回答2:


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);
           }
   }
}



回答3:


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);


来源:https://stackoverflow.com/questions/41824920/how-to-serialize-json-with-array-field-to-object-with-string-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!