Jackson Json Object mapper : how to map an array? - json

I am trying to map an array from a backend api call. How can I map this data knowing that :
the following classes will be used to hold the json array data :
#Data
private static class UserListBean {
private List<UserBean> userList;
}
#Data
private static class UserBean {
private String id;
private String userName;
private String password;
}
the json will have the following format (the following example just have one item in it) :
[
{
"id":1,
"userName":"bob",
"password":"403437d5c3f70b1329f37a9ecce02adbbf3e986"
}
]
I am using Jackson and I have tried the following so far but it keeps sending me back an exception
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
final ObjectReader reader = mapper.reader(UserListBean.class);
GeoHubAccountListBean accounts = null;
try {
accounts = reader.readValue(jsonString);
} catch (final IOException ex) {
log.error("Cannot convert JSON into a list of users", ex);
}
Here the final ObjectReader reader = mapper.reader(UserListBean.class); throws an exception
Can not deserialize instance of com.xxx.XXX$UserListBean out of START_ARRAY token
Any idea ?
thanks

Well, if you are trying to deserialize json to an object of type UserListBean, then you need to deserialize a JSONObject (Java Objects tend to map to JSONObjects).
Therefore, your outer most json construct should be an object. Your outer most json construct is a JSONArray.
Your UserListBean has a single field, which is a List<UserBean>. So your top level json construct (which is a JSONObject) should contain a single field with the name 'userList' with a value that is a JSONArray (Java Collections tend to map to JSONArrays).
I think this is the actual json you are looking for:
{
"userList":[
{
"id":1,
"userName":"bob",
"password":"403437d5c3f70b1329f37a9ecce02adbbf3e986"
}
]
}
If you have no control over the json coming in, then you probably want to ditch the parent object UserListBean and deal directly with the List<UserBean>, as that would work with the json you have provided.

Try something like the following:
public static <T> T mapFromJson(String json, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper.readValue(json, clazz);
}
public static void main(String[] args) throws IOException {
HeaVO HeaVO=new HearVO();
HeaVO.setId("Id");
List<HeaVO> listVO=new ArrayList<HeaVO>();
listVO.add(HeaVO);
List<HeaVO> listHeaVORes=Arrays.asList(mapFromJson(mapToJson(listHeaVO), HeaVO[].class));
System.out.println(((HeaVO)listVORes.get(0)).getId());
}

Related

Gson throws exception when expected string array returns object

I'm using an api where a particular field (below) normally contains a string array. However, if the array is empty, the api returns an empty object in what is normally the array of string. Here is the field that's causing problems.
Normal.
"a": [
"str"
]
Empty.
"a": [
{}
]
The second case causes Gson to crash with a JsonSyntaxException. How do I handle this?
I don't know if this is the best way, but it works.
The faulty field can be annotated with #JsonAdapter(MyTypeAdapter.class). The TypeAdapter can then use its read method and check using peek() weather or not the next value is of the expected type.
Let us assume you have a class representing the API response, like:
public class Response {
private String[] a;
private String b;
private String c;
}
One way to get the Response object parsed whether JSON for a is valid or not is to create a JsonDeserializer that checks if a can parsed and excludes parsing of a if it fails, so leaves a to null.
public class SkipBadSyntaxDeserializer implements JsonDeserializer<Response> {
// This strategy is used if parse of field a fails
private final ExclusionStrategy excludeA = new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return "a".equals(f.getName());
}
// no need to care of this used only here for the Response class
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
};
// one parser for good and another for bad format
private final Gson gson = new Gson(),
gsonBadFormat = new GsonBuilder()
.addDeserializationExclusionStrategy(excludeA).create();;
#Override
public Response deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
try {
return gson.fromJson(json, Response.class);
} catch (JsonSyntaxException e) {
// parse a failed try again without it
return gsonBadFormat.fromJson(json, Response.class);
}
}
}
Try it with:
new GsonBuilder().registerTypeAdapter(Response.class,
new SkipBadSyntaxDeserializer())
.create()
.fromJson(JSON, Response.class);
If JSON would be like:
{
"a": [{}],
"b": "bval",
"c": "cval"
}
then properties for Response would be:
a=null
b="bval"
c="cval"
Update
Based on your own answer: if it is possible to alter DTO for response then using annotation #JsonAdapter will let you to handle this per field. Deserializer will then be simply:
public class SkipExceptionAdapter implements JsonDeserializer<String[]> {
#Override
public String[] deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
try {
return context.deserialize(json, String[].class);
} catch (JsonSyntaxException e) {
return new String[] {}; // or null how you wish
}
}
}
and annotation in Response.a
#JsonAdapter(SkipExceptionAdapter.class)
private String[] a;
will handle it for that field only.

Gson: Is there more efficient ways to do custom (de)serialization of a verbose Json object

I am working with some json objects that I call verbose:
{
"user": {
"name": "username",
"email": "blah#blah.com",
"time_zone": "America/New_York"
}
}
But I'd prefer to just deal with them in terms of java POJOs like:
class UserDetails {
String name;
String email;
String timeZone;
...
}
Note that I have no control over the POJO as it is generated code.
My two requirements for (de)serialization is that
the timeZone field maps to time_zone in JSON
the outer user is ignored
So I have some customer (de)serializers:
class UserDeserializer implements JsonDeserializer<UserDetails> {
#Override
public UserDetails deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonElement content = je.getAsJsonObject().get("user");
UserDetails userDetails = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
.fromJson(content, UserDetails.class);
return userDetails;
}
}
class UserSerializer implements JsonSerializer<UserDetails> {
#Override
public JsonElement serialize(UserDetails userDetails, Type typeOfSrc,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
JsonElement je = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create().toJsonTree(userDetails);
obj.add("user", je);
return obj;
}
}
I feel like creating new Gson objects in the (de)serializer logic is not ideal/efficient just to add and remove the outermost user key.
EDIT: Actually .setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) does work fine on deserialization.
I don't really think it's a good idea in general, and you should probably better have a single Wrapper<T> for all "top-most" purposes (if you don't want your inner objects to be considered "verbose").
But you're right when you say
I feel like creating new Gson objects in the (de)serializer logic is not ideal/efficient just to add and remove the outermost user key.
So:
Creating a Gson is a relatively expensive operation.
This just creates unnecessary objects and hits the heap.
Gson may be configured in a special way and you might want to share the same Gson configuration everywhere.
JsonSerializer and JsonDeserializer operate on JSON trees (JsonElement and its subclasses), therefore it creates an intermediate in-memory tree representations before/after serialization/deserialization.
You might consider a faster solution, that's free of those items.
final class VerboseTypeAdapterFactory
implements TypeAdapterFactory {
private final Map<Class<?>, String> mappings;
private VerboseTypeAdapterFactory(final Map<Class<?>, String> mappings) {
this.mappings = mappings;
}
static TypeAdapterFactory get(final Map<Class<?>, String> mappings) {
// Create a defensive copy to make sure the map is not modified from outside
final Map<Class<?>, String> mappingsCopy = mappings
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return new VerboseTypeAdapterFactory(mappingsCopy);
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawType = typeToken.getRawType();
// Not something we can handle?
if ( !mappings.containsKey(rawType) ) {
// Then let Gson do its job elsewhere
return null;
}
// Getting a property name we want to use for a particular class
final String propertyName = mappings.get(rawType);
// And getting the original type adapter for this class (effectively ReflectiveTypeAdapterFactory.Adapter)
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return VerboseTypeAdapter.get(propertyName, delegateTypeAdapter);
}
private static final class VerboseTypeAdapter<T>
extends TypeAdapter<T> {
private final String propertyName;
private final TypeAdapter<T> delegateTypeAdapter;
private VerboseTypeAdapter(final String propertyName, final TypeAdapter<T> delegateTypeAdapter) {
this.propertyName = propertyName;
this.delegateTypeAdapter = delegateTypeAdapter;
}
private static <T> TypeAdapter<T> get(final String propertyName, final TypeAdapter<T> delegateTypeAdapter) {
return new VerboseTypeAdapter<>(propertyName, delegateTypeAdapter)
// A convenient method to simplify null-handling
.nullSafe();
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final T object)
throws IOException {
// Open the object with `{`
out.beginObject();
// Prepend the object with its reserved name
out.name(propertyName);
// Write the object
delegateTypeAdapter.write(out, object);
// And close the object with `}`
out.endObject();
}
#Override
public T read(final JsonReader in)
throws IOException {
// Assume the very first token is `{`
in.beginObject();
// Peeking what's the actual property name
final String actualPropertyName = in.nextName();
// And if it's not we expect, throw a JSON parse exception
if ( !actualPropertyName.equals(propertyName) ) {
throw new JsonParseException("Expected " + propertyName + " but was " + actualPropertyName);
}
// Otherwise read the value led by the property name
final T object = delegateTypeAdapter.read(in);
// And make sure there are no more properties
if ( in.hasNext() ) {
throw new JsonParseException(propertyName + " is expected to be the only top-most property");
}
// Assume the very last token is `}` (this works for the check above, but we made it more semantical)
in.endObject();
return object;
}
}
}
So, for example, the following code
private static final Gson gson = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapterFactory(VerboseTypeAdapterFactory.get(ImmutableMap.of(UserDetails.class, "user")))
.create();
...
final UserDetails userDetails = gson.fromJson(jsonReader, UserDetails.class);
System.out.println(userDetails.name);
System.out.println(userDetails.email);
System.out.println(userDetails.timeZone);
final String json = gson.toJson(userDetails);
System.out.println(json);
produces
username
blah#blah.com
America/New_York
{"user":{"name":"username","email":"blah#blah.com","time_zone":"America/New_York"}}
As the conclusion:
No more excessive Gson instantiation.
Original Gson instance configuration inherited (i.e. FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES set once).
No intermediate JsonElement instances.

Custom Jackson Deserializers and Arrays

Please note: There are many questions on this site about how to use custom Jackson deserializers...this question is not one more of those! This questions has to do with using a deserializer under very unique circumstances (none of which have previous questions/answers on this site!).
Spring Boot using Jackson for JSON serialization here. I have two POJOs that are used in the #RequestBody (HTTP request entity) for a POST endpoint:
#JsonDeserialize(using = FizzDeserializer.class)
public class Fizz {
private String name;
private String label;
private Integer code;
// Getters, setters & ctors
}
#JsonDeserialize(using = BuzzDeserializer.class)
public class Buzz {
private String id;
private String locale;
private Set<Fizz> fizzes;
// Getters, setters & ctors
}
#RestController
#RequestMapping("v1/data/buzzes")
public class BuzzController {
#PostMapping
public void updateBuzz(#RequestBody Buzz buzz) {
// do whatever
}
}
I want HTTP clients to be able to POST the following JSON to this endpoint:
{
"id" : "12345-67890",
"locale" : "en_US",
"fizzes" : [
"foo",
"bar"
]
}
...where "foo" and "bar" are the Fizz#names of two different Fizz instances. In other words, I don't want the client to have to specify the entire Fizz object, just specify its name as a JSON string (my app + DB guarantee Fizzes have unique names).
So I'm using a custom JsonDeserializer to accomplish all this mapping:
public BuzzDeserializer extends JsonDeserializer<Buzz> {
Buzz deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode buzzNode = jsonParser.readValueAsTree();
String id = buzzNode.get("id");
String locale = buzzNode.get("locale");
// TODO: How to read "foo" and "bar" (etc.) into a Set<Fizz> instances?
Set<Fizz> fizzes = ???
new Buzz(id, locale, fizzes);
}
}
public FizzDeserializer extends JsonDeserializer<Fizz> {
private FizzDAO fizzDAO;
// Getters, setters & ctors...
Fizz deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode fizzNode = jsonParser.readValueAsTree();
// If I can get access to "foo"/"bar"/etc. string somehow, I can look up the Fizz using the DAO:
String fooBarEtcStr = ???
return fizzDAO.findFizzByName(fooBarEtcStr);
}
However I'm not sure how I can read the JSON fizzes array into a Set<Fizz> inside this deserializer. Any ideas?
Taken from the comments to the question, the only problem seems to be to get the JSON array. Provided that the service or DAO to lookup is already injected or provided in the serializer, try something along the lines:
final JsonNode arr = buzzNode.get("fizzes");
if (arr.isArray()) {
final Set<Fizz> fizzes = Sets.newHashSetWithExpectedSize(arr.size());
for (JsonNode obj : arr) {
final String name = obj.asText();
Fizz fizz = // load from DAO
fizzes.add(fizz);
}
}
This can of course be optimised by collecting the String values and use only one DAO call. Also some java8 streaming could make the code less verbose.

How to use Jackson Annotations to process JSON with random object names

I have the following JSON:
{
"animals": {
"113110": {
"id": 113110,
"name": "Dog",
.....
},
"121853": {
"id": 121853,
"name": "Cat",
.....
}
}
}
Ideally, the JSON should be as follows and implementing Jackson annotations will be trivial:
{
"animals": [
{
"id": 113110,
"name": "Dog",
.....
},
{
"id": 121853,
"name": "Cat",
.....
}
]
}
However, is there a way to use Jackson to abstract the object names so I can work with the original JSON, if anybody gets my meaning?
EDIT:
I do not know how to create my POJO. I could create an Animal class, with objects 113110 and 121853, but as these objects will always vary, how do I use Jackson annotations in my Animal class so that I can deserialize the JSON?
Thanks all, but I couldn't really understand the rest of the answers ( I don't really want to delve into Jackson, I just want to convert it to a POJO), so I found an alternative solution.
I left out a key bit of information: The JSON I posted is part of a much larger JSON object.
I ended up using Jackson's #AnySetter as I noticed that any "un-parsable" JSON data related to "animals" could be retrieved in additionalProperties defined as follows in its parent class:
public class AnimalParent {
#JsonIgnore
private Animal animal;
#JsonIgnore
private Map<String, Object> additionalProperties =
new HashMap<String, Object>();
public Animal getAnimal() {
return this.animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Then in my main method where I parse the parent JSON, I have the following after the parsing is completed to parse the animals.
// start parsing parent JSON
...
// end parsing parent JSON
// parse animal
ObjectMapper mapper = new ObjectMapper();
if (animalParent.getAdditionalProperties() != null) {
for (Map.Entry<String, Object> item : animalParent
.getAdditionalProperties().entrySet()) {
Animal animal = mapper.convertValue(item.getValue(), Animal.class);
animalParent.setAnimal(animal);
}
It is always a bit messy when your JSON is "dynamic" as per the OPs example. The main approaches are
parse the JSON to some kind of dynamic Map-structure
parse the JSON to a tree-structure (i.e. JsonNode)
use a custom deserializer to parse the JSON and map it to a POJO
There are downsides to all of these approaches. The Map-approach offers no type safety and does not offer much functionality when it comes to traversing the object structure.
The JsonNode approach offers some nice type-methods and also some traversal methods. IMO this is a cleaner approach than the Map-approach.
The POJO-approach is type safe but a custom deserializer is required which is generally not pretty...
So, maybe the following "hybrid" approach can be of use.
// Setup the mapper
final ObjectMapper mapper = new ObjectMapper();
// Parse the json to a tree (JsonNode). This is IMO nicer than the
// Map since it exposes some nice methods for managing the
// underlying data
final JsonNode json = mapper.readTree(jsonString);
// Alt 1, use JsonNode directly
for (final JsonNode animal : json.path("animals")) {
final int id = animal.get("id").asInt();
final String name = animal.get("name").asText();
// Do stuff with name and id...
}
If the JsonNode approach feels a bit too raw then it is possible to convert the JsonNode object to a POJO without the use of a deserializer. If you assume the following POJO:
public class Animal {
private final int id;
private final String name;
#JsonCreator
public Animal(#JsonProperty("id") final int id, #JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
Then, this code can be used for converting to POJOs:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(jsonString);
// Alt 2, convert to a Pojo
for (final JsonNode animal : json.path("animals")) {
final Animal a = mapper.treeToValue(animal, Animal.class);
// Handle the animal instance...
}
Finally, if the POJO still contains dynamic data you can use the following approach to handle that. In your POJO, declare the following:
private final Map<String, Object> dynamic = new HashMap<>();
#JsonAnySetter
private void set(String name, Object value) {
dynamic.put(name, value);
}
Note that it is not a must for the method to be public (i.e. it can be hidden from the outside world). This way you'll get hold of all the unknown/dynamic JSON elements.
Personally, any time I'm dealing with weird JSON that doesn't map easily to POJOs, I just do custom serialization.
I would probably make the POJOs look something like this:
public class Animal
{
String id;
String name;
}
public class JsonThing
{
List<Animal> animals;
}
Then I would implement a custom parser using the Jackson stream API. Here's a quick stub of a JsonDeserializer<JsonThing>:
public Stuff deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
.... // Start by creating a JsonThing instance and init the list.
while (jp.nextToken() != JsonToken.END_OBJECT)
{
jp.nextToken();
switch (jp.getCurrentName())
{
case "animals":
jp.nextToken(); // Skip to {
jp.nextToken(); // Skip id field
Animal a = jp.readValuesAs(Animal.class);
// Add to list
}
}
..... // Return JsonThing
}
If the keys are not known in advance then use Map instead of POJO.
Have a look at Example 1 and Example 2
You can try any one.
sample code: (using Jackson Library)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
sample code: using GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
Perhaps it's just a question of combining simple Maps with pojos? Like:
public class Wrapper {
public Map<Long, Animal> animals;
}
public class Animal {
public long id;
public String name;
}
and that's it; although ids there match, maybe there is no need to try to model that dependency.

gson: Add function result to an object created by toJson()

gson is such a great serialize/deserialization tool. It's really simple to get a JSON representation of an arbitrary object by using the toJson-function.
Now I want to send the data of my object to the browser to be used within javascript/jQuery. Thus, I need one additional JSON element defining the dom class of the object which is coded within my object as a dynamic/memberless function
public String buildDomClass()
How to add this string to my String created by the toJson function?
Any ideas?
Thanks a lot
An easy way is to combine a TypeAdapterFactory and an interface.
First an interface for your method :
public interface MyInterface {
public String buildDomClass();
}
then the factory :
final class MyAdapter implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> tokenType) {
final TypeAdapter<T> adapter = gson.getDelegateAdapter(this, tokenType);
return new TypeAdapter<T>() {
#Override
public T read(JsonReader reader) throws IOException {
return adapter.read(reader);
}
#Override
public void write(JsonWriter writer, T value) throws IOException {
JsonElement tree = adapter.toJsonTree(value);
if (value instanceof MyInterface) {
String dom = ((MyInterface) value).buildDomClass();
JsonObject jo = (JsonObject) tree;
jo.addProperty("dom", dom );
}
gson.getAdapter(JsonElement.class).write(writer, tree);
}
};
}
}
Easy to understand, if the object you want to serialize implement the interface, you delegate the serializing, and then you add an extra property holding you DOM.
In case you don't know, you register a factory like this
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new MyAdapter()).create();