问题
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:
- 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);
}
}
- 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