GSON fails to read/map a field with name like USERId

0 votes
Hi,

Response from a client (rest call) has a json element like USERId.
The related java variable is as below
@JsonProperty("USERId")
private String uSERId;

I am using spring RestTemplate to make this rest call and for mapping from JSON to JAVA I am using GSON.
I have completely excluded jackson databind which is spring rest template's default mapper like below.

this.getMessageConverters().removeIf(converter -> converter.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
GsonHttpMessageConverter messageConverter = new GsonHttpMessageConverter();
messageConverter.setGson(gson);
this.getMessageConverters().add(messageConverter);

This way, I made sure only GSON is used for mapping.

When I directly hit client rest service on postman, I see this filed has a value - "USERId": "025076373".
However the response object from RestTemplate doesnt have this field i.e., all other fields with proper java canonical naming are mapped properly.
I mean all other fields in the respone object are mapped with values from response json. Response object doesnt have uSERId or the corresponding value.

I believe this issue is probably because of the weird naming convention, however the same works fine with MappingJackson2HttpMessageConverter.

I have tried debugging by writing an adapter and assigning it to gsonBuilder and then setting that to GsonHttpMessageConverter.
gsonBuilder.registerTypeHierarchyAdapter(String.class, new StringTypeAdapter().nullSafe())

While debugging the adapter, I could see all other fields except USERId.

Object mapper of jackson databind has a property MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, which I believe is the reason why the same field is working jackson.
I couldnt find a similar property with GSON.
Any pointers on how to fix this issue would be very helpful.

Technologies used :
spring boot
spring rest template
com.google.code.gson:gson:2.8.6
Jul 1 in Java by RamaKrishnaRaju
• 120 points
89 views

1 answer to this question.

0 votes

Hello @ RamaKrishnaRaju,

You should note the Javadoc of configureMessageConverters states first as below.

Configure the HttpMessageConverters to use for reading or writing to the body of the request or response. If no converters are added, a default list of converters is registered.

Note that adding converters to the list, turns off default converter registration. To simply add a converter without impacting default registration, consider using the method extendMessageConverters(java.util.List) instead.

In other words, you've removed all the converters that handle other content types.

Note that Spring MVC only registers the Jackson HttpMessageConverter (MappingJackson2HttpMessageConverter) if the corresponding Jackson libraries are in your classpath. You could remove them and, assuming you have Gson in your classpath, a GsonHttpMessageConverter will be registered for you.

From your code, it seems you want to create a custom GsonHttpMessageConverter. In that case, you can follow the Javadoc instructions and use extendMessageConverters.

A hook for extending or modifying the list of converters after it has been configured. This may be useful for example to allow default converters to be registered and then insert a custom converter through this method.

You'd first want to remove the existing instance, then add your own. For example,

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    // remove the Jackson version if Jackson is still in your classpath
    converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
    // remove the existing instance (from defaults)
    converters.removeIf(converter -> converter instanceof GsonHttpMessageConverter);
    // add your custom
    converters.add(createGsonHttpMessageConverter());
}

Hope it helps!!

answered Jul 1 by Niroj
• 41,480 points
Hi Niroj,

Thanks for taking time to help me with this issue.

I am able to manipulate GsonHttpMessageConverter, but the problem here is gson is not mapping json element USERId.in response to a related java variable below
@JsonProperty("USERId")
private String uSERId;

The same was working fine with MappingJackson2HttpMessageConverter.

Hello @RamaKrishnaRaju,

Can you please provide your snippet or code?

Adapter Code : 

import java.io.IOException;

import java.util.Arrays;

import java.util.List;

import com.google.gson.TypeAdapter;

import com.google.gson.stream.JsonReader;

import com.google.gson.stream.JsonToken;

import com.google.gson.stream.JsonWriter;

public class StringTypeAdapter extends TypeAdapter<String> {

@Override

public void write(JsonWriter out, String value) throws IOException {

if (value == null) {

    out.nullValue();

} else {

out.value(new String(value));

}

}

@Override

public String read(JsonReader in) throws IOException {

System.out.println("In String type adapter");

if (in.peek() == JsonToken.NULL) {

in.nextNull();

return null;

}

while (in.hasNext()) {

JsonToken next = in.peek();

if(next == JsonToken.NULL) {

in.nextNull();

return null;

}

switch(next) {

case BEGIN_ARRAY:

System.out.println("BEGIN_ARRAY");

String key = in.nextString();

System.out.println("BEGIN_ARRAY - Key :"+key);

break;

case END_ARRAY:

System.out.println("END_ARRAY");

String key2 = in.nextString();

System.out.println("END_ARRAY - Key2 :"+key2);

break;

case BEGIN_OBJECT:

System.out.println("BEGIN_OBJECT");

String key3 = in.nextString();

System.out.println("BEGIN_OBJECT - Key3 :"+key3);

break;

case END_OBJECT:

System.out.println("END_OBJECT");

String key4 = in.nextString();

System.out.println("END_OBJECT - Key4 :"+key4);

break;

case NAME:

System.out.println("NAME");

if(next == JsonToken.NULL) {

in.nextNull();

System.out.println("JsonToken.NULL");

return null;

}

String key5 = in.nextName();

if("indicator".equals(key5)){

System.out.println("NAME - Key5 : " +key5);

break;

}

if("USERId".equals(key5)) {

String value2 =Double.toString(in.nextDouble());

System.out.println("NAME - Key5 : " +key5+ " Value : "+value2);

}

if("amount".equals(key5)) {

String value2 =Double.toString(in.nextDouble());

System.out.println("NAME - Key5 : " +key5+ " Value : "+value2);

}

break;

case STRING:

System.out.println("STRING");

String value = in.nextString();

System.out.println("STRING - Value : "+value);

break;

case BOOLEAN:

//System.out.println("BOOLEAN");

break;

case NUMBER:

System.out.println("NUMBER");

String key6 = Double.toString(in.nextDouble());

if("cashAmount".equals(key6)) {

break;

}

System.out.println("NUMBER - Key6 :" +key6);

case END_DOCUMENT:

System.out.println("END_DOCUMENT");

String key7 = in.nextName();

System.out.println("END_DOCUMENT - Key7 :"+key7);

break;

default:

System.out.println("DEFAULT");

break;

}

}

return null;

}

}

Gson converter code :

GsonBuilder gsonBuilder = new GsonBuilder();

gsonBuilder.registerTypeHierarchyAdapter(String.class, new StringTypeAdapter().nullSafe());

gson = gsonBuilder.create();

GsonHttpMessageConverter messageConverter = new GsonHttpMessageConverter();

messageConverter.setGson(gson);

this.getMessageConverters().add(messageConverter);

this.getMessageConverters().removeIf(converter -> converter.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));

ResponseEntity<Response> responseEntity = exchange(url, HttpMethod.POST, httpEntity, Response.class);

Client Response json : 

{

    "Response": {

        "responseText": null

        "userList": [

            {

                "name": "Test"

                "mobile": 123456789,

                "class": 12,

                "gpa": 8.80,

                "joinDate": "2015-08-28",

                "USERId": "025076373"

            }

]

}

}

In the above json response, all other fields are shown in the StringTypeAdapter while debugging. USERId value never came in any of the switch statements.

Hello @ RamaKrishnaRaju,

Try to configure individual ObjectMappers like this so :

ObjectMapper mapper  = new ObjectMapper();
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));

By default the ObjectMapper serializes everything and a Mixin would have to specify which properties to ignore.

Hope it helps!!

Thank you!!

If you want it set globally, I usually access a configured mapper through a wrapper class.

Related Questions In Java

+5 votes
3 answers

How to execute a python file with few arguments in java?

You can use Java Runtime.exec() to run python script, ...READ MORE

answered Mar 27, 2018 in Java by DragonLord999
• 8,420 points

edited Nov 6, 2018 by Omkar 26,424 views
0 votes
3 answers

How to read a Text File in Java?

You can use readAllLines and the join method to ...READ MORE

answered Jul 27, 2018 in Java by samarth295
• 2,190 points
291 views
0 votes
2 answers

How to read a text file in Java?

You can use Scanner class to read ...READ MORE

answered Aug 9, 2018 in Java by Parth
• 4,630 points
121 views
0 votes
1 answer

Is it possible to create a memory leak with Java

Here's a good way to create a memory ...READ MORE

answered May 30, 2018 in Java by Parth
• 4,630 points
1,181 views
0 votes
1 answer

What is the simplest way to read JSON from a URL in java

Read json from url use url.openStream() and read contents ...READ MORE

answered Jun 13, 2018 in Java by samarth295
• 2,190 points
1,537 views
0 votes
1 answer

How to tell Jackson to ignore a field during serialization if its value is null?

To suppress serializing properties with null values ...READ MORE

answered Jul 4, 2018 in Java by Rishabh
• 3,580 points
4,203 views
0 votes
2 answers

How to left pad a string with zero in java?

String paddedString = org.apache.commons.lang.StringUtils.leftPad("129018", 10, "0") the second ...READ MORE

answered Aug 31, 2018 in Java by Sushmita
• 6,890 points
1,181 views
0 votes
1 answer
0 votes
1 answer

How to run a Java program from the command line on Windows?

Hello @kartik, In case your Java class is ...READ MORE

answered Apr 9 in Java by Niroj
• 41,480 points
84 views
0 votes
1 answer

How to call setUndecorated() after a frame is made visible?

Hello kartik, You can Try: dispose(); setUndecorated(true); setVisible(true); Check it Out. Hope ...READ MORE

answered Apr 21 in Java by Niroj
• 41,480 points
104 views