Unable to deserialize a field when using FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES - gson

I am using gson for deserialization. I have a field with the name "listName" it has #Expose annotation.
When I set FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES in GsonBuilder and create Gson object using this GsonBuilder, then that field is not getting deserialized.
When I do not use FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES then that field is getting serialized.
Why is this happening?

The json field name should be "list_name" and your pojo object would have the field listName, when you are using that FieldNamingPolicy.
JSON:
{
"list_name": ""
}
POJO CLASS:
class POJO
{
String listName;
}
IF, when you do not use FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES and that field IS getting deserialized [sic]... then my guess is that your json field name is also "listName".

Related

Deserialize Long(Epoch Time) to Java Date Object using GSON

I am subscribing to an SQS and the message body has a few epoch times. It looks like below
"createdAt" : 1660744139,\n "updatedAt" : 1660744139,\n
I have a Java POJO which contains 2 fields
Date createdAt;
Date updatedAt;
I am using GSON to deserialize the SQS message body to my POJO, but it's failing with an error com.google.gson.JsonSyntaxException: 1660744139.
My JSON instantiation looks like
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
If I understand it right, the error is being thrown because I am trying to deserialize a Long type to Date. What will be the correct way to deserialize?
I have seen suggestions to instantiate my GSON object with dateFormatter, but not sure if that will work for my case. Thank you.

RestTemplate fetch nested json from response

I need to consume a REST endpoint using spring RestTemplate. Below is the sample response from the endpoint and I need to fetch nested employee json and map it to model object. I found various complex solutions like
Deserializing response to a wrapper object and then fetching Employee object from it
Getting the response as a string and then converting it to json and deserializing
None of these solutions are simple and clean. Not sure if there is a clean and automatic way of doing it like this
ResponseEntity<Employee> response = restTemplate.exchange(URL,..?..);
Response -
{
"employee": {
"id": "123",
"first_name": "foo",
"last_name": "bar"
},
"session": {
"id": "1212121",
"createdDate": "2022-08-18T19:35:30Z"
}
}
Model object -
public class Employee {
private long emplId;
private String fName;
private String lName;
}
RestTemplate can do all of this for you, but first, your object doesn't match your response.
Ideally, you would have a Class that has an Employee and Session, like this
public class HaveSomeClassMan {
private Employee employee;
private Session session;
}
Then you can do
HaveSomeClassMan haveSomeClassMan = restTemplate.postForObject("URL", "requestObject", HaveSomeClassMan.class);
But, since you already have a JSON string, here's how you can convert it to a JSONObject, get the Employee out of it, and use the ObjectMapper to convert it to an Employee object.
JSONObject jsonObject = new JSONObject(s); // where s is your json string
String theEmployee = jsonObject.get("employee").toString(); // get the "employee" json from the JSONObject
Employee employee = new ObjectMapper().readValue(theEmployee, Employee.class); // Use the object mapper to convert it to an Employee
You do need to pay attention, though, as your model doesn't line up with your json. Your model calls the first name field, fname, yet your json has this as first_name. These properties all need to line up. If you want to keep your model as you've defined it, you need to annotate your fields with what their corresponding json field is, like this:
#JsonProperty("first_name")
private String fName;
This tells the mapper to map the JSON field first_name to the property fName. Regardless of whether you let RestTemplate do the mapping for you or you do it manually via the JSONObject approach, your model needs to match your JSON string. You can do this implicitly just by naming the fields in your model the same as they are in your JSON, or explicitly by adding the #JsonProperty to each field that doesn't implicitly map.

How to make a custom converter for a String field using Spring Data Elasticsearch?

I am using Spring Data Elasticsearch in my project. I have a class like this,
#Document("example")
class Example {
#Id
private String id;
private String name;
private String game;
private String restField;
}
What I need is when ever I am saving the Example object to elasticsearch I am removing a value. But when I am getting the data from elasticsearch I need that removed value to be appended.
save(Example example) {
example.setRestField(example.getRestField().replace("***", ""));
exampleRepository.save(example);
}
get(String id) {
Example example = exampleRepository.findById(id);
example.getRestField().concat("***");
return example;
}
Right now I am doing like the above way. But can we use a custom converter for this? I checked the converter examples for Spring Data Elasticsearch but those are for different different objects. How I can create a custom converter only for this particular String field restField? I don't want to apply this converter for other String fields.
Currently there is no better solution. The converters registered for Spring Data Elasticsearch convert from a class to a String and back. Registering a converter for your case would convert any String property of every entity.
I had thought about custom converters for properties before, I have created a ticket for this.
Edit 05.11.2021:
Implemented with https://github.com/spring-projects/spring-data-elasticsearch/pull/1953 and will be available from 4.3.RC1 on.

How to parse Dynamic lists with Gson annotations?

I am using Retrofit and Gson to query an API, however I have never come across a JSON response like it.
The Response:
{
Response: {
"Black":[
{"id":"123","code":"RX766"},
{"id":"324","code":"RT344"}],
"Green":[
{"id":"3532","code":"RT983"},
{"id":"242","code":"RL982"}],
"Blue":[
{"id":"453","code":"RY676"},
{"id":"134","code":"R67HJH"}]
}
}
The problem is the list elements id eg "black" is dynamic, so I a have no idea what they will be.
So far I have created a class for the inner type;
class Type {
#SerializedName("id") private String id;
#SerializedName("code") private String code;
}
Is it possible to have the following?
class Response {
#SerializedName("response")
List<Type> types;
}
And then move the list ID into the type, so the Type class would become;
class Type {
#SerializedName("id") private String id;
#SerializedName("code") private String code;
#SerializedName("$") private String type; //not sure how this would be populated
}
If not, how else could this be parsed with just Gson attributes?
Ok so I have just seen this question;
How to parse dynamic JSON fields with GSON?
which looks great, is it possible to wrap a generic map with the response object?
If the keys are dynamic you want a map.
class Response {
#SerializedName("response")
Map<String, List<Type>> types;
}
After deserialization you can coerce the types into something more semantic in your domain.
If this is not suitable you will need to register a TypeAdapter or a JsonDeserializer and do custom deserialization of the map-like data into a simple List.

Use a custom deserializer only on fields?

I do a replication mechanism where I synchronize two databases. For communicating between databases I serialize the objects into JSON using Gson. Each object has a UUID to identify it. To avoid having to send the items that are up to date I use the objects UUID when an object is included in a field in an object to be replicated.
We got the following classes:
public class Entity {
String uuid;
// Getters and setters..
}
public class Article extends Entity {
String name;
Brand brand;
// Getters and setters..
}
public class Brand extens Entity {
String name;
Producer producer
// Getters and setters..
}
public class Producer extends Entity {
String name;
// Getters and setters..
}
If I serialize Article its JSON representation will look like this:
{"brand":"BrandÖ179d7798-aa63-4dd2-8ff6-885534f99e77","uuid":"5dbce9aa-4129-41b6-a8e5-d3c030279e82","name":"Sun-Maid"}
where "BrandÖ179d7798-aa63-4dd2-8ff6-885534f99e77" is the class ("Brand") and the UUID.
If I serialize Brand I expect:
{"producer":"ProducerÖ173d7798-aa63-4dd2-8ff6-885534f84732","uuid":"5dbce9aa-4129-41b6-a8e5-d3c0302w34w2","name":"Carro"}
In Jackson I would change Article class to:
public class Article {
String uuid;
String name;
#JsonDeserialize (using = EntityUUIDDeserializer.class) # JsonSerialize (using = EntityUUIDSerializer.class)
Brand brand;
// Getters and setters..
}
and implement custom serializer and deserializer to return the UUID instead of the object.
Gson do not have a #JsonDeserialize annotation.
If we install the serializer and deserializer doing like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Producer.class, new EntityUUIDDeserializer())
.registerTypeAdapter(Brand.class, new EntityUUIDDeserializer())
.registerTypeAdapter/Producer.class, new EntityUUIDSerializer())
.registerTypeAdapter(Brand.class, new EntityUUIDSerializer())
.create();
We can serialize Article and Brand ok.
Deserialize Article by
Article article= gson.fromJson(inputJSONString, Article.class);
works fine but
Brand brand= gson.fromJson(inputJSONString, Brand.class);
do not work. I guess the probem is that when we deserialize a Brand we get the deserializer for Brand to kick in trying to return an UUID string, but we want the deserializer to return a Brand-object instead.
Is there a way to avoid creating two different Gson objects? The problem with two diffrent Gson objects is when you want to deserialize an object that contains both Article and Brand.
You write the serializer/deserializer and register it with Gson (using the GsonBuilder).
https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization
Gson g = new GsonBuilder()
.registerTypeAdapter(Producer.class, new MyProducerDeserializer())
.registerTypeAdapter(Producer.class, new MyProducerSerializer())
.create();
When you serialize/deserialize your Brand class, it will use them for the Producer field contained therein.

Resources