'How to map a JSON string which includes members named long and short

I need to map a JSON string which includes values named long and short:

"status": {
   "long": "Finished",
   "short": "F",
   "elapsed": 90
}

I tried the following class:

public class Status {

    @JsonProperty("long")
    public String _long;
    @JsonProperty("short")
    public String _short;
    @JsonProperty("elapsed")
    public Object elapsed;

}

with the command:

objectMapper.readValue(resBody, Response.class);

response contains the status part:

{
    "response": {
        "id": 157016,
        "timezone": "UTC",
        "date": "2019-08-10T11:30:00+00:00",
        "timestamp": 1565436600,
        "status": {
            "long": "Long Value",
            "short": "LV",
            "elapsed": 20
        }
    }
}

But still I get the following error:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "long"

How can this be fixed? I do not have control on the JSON format.



Solution 1:[1]

Well, it is obviously not a perfect solution to the problem, but one may find it as a helpfull workaround:

Since I don't care about these values, I'll just rename their names and adapt the class members name accordingly:

on the json string resBody I get as a response I will do the following

@NotNull 
private static String mitigateLongAndShortValueNames(String resBody) { 
   resBody = resBody.replaceAll("\"long\":", "\"longValue\":"); 
   resBody = resBody.replaceAll("\"short\":", "\"shortValue\":"); 
   return resBody; 
} 

and change

public class Status {

    @JsonProperty("long")
    public String _long;
    @JsonProperty("short")
    public String _short;
    @JsonProperty("elapsed")
    public Object elapsed;

}

to

public class Status {

    public String longValue;
    public String shortValue;
    public Object elapsed;

}

It worked for me!

Solution 2:[2]

One of the ways to solve the problem is select the json part you are interested combining the ObjectMapper#readTree method that converts your json to a JsonNode object and then select the part of the JsonNode object you are looking for with the JsonNode#at method which matches the /response/status path expression like below:

//it contains only the status labelled node of your json
JsonNode statusNode = mapper.readTree(json).at("/response/status");   

After you can use the ObjectMapper#treeToValue method to convert the JsonNode to your Status class obtaining the expected result:

JsonNode statusNode = mapper.readTree(json).at("/response/status");
Status status = mapper.treeToValue(statusNode, Status.class);
//ok, it prints {"long":"Long Value","short":"LV","elapsed":20}
System.out.println(mapper.writeValueAsString(status));

Solution 3:[3]

The full exception message should look something like this: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "long" (class ...), not marked as ignorable (n known properties: ...)

If it is the case that you're not interested in certain properties, it's easier and cleaner to ignore those during parsing. Either with an annotation on the data class or a DeserializationFeature on the objectmapper.

See Jackson with JSON: Unrecognized field, not marked as ignorable

Solution 4:[4]

Extending the comment: there's no problem with Status and "status":... parts, @JsonProperty() is meant to handle the renaming. You probably pass the wrong class to readValue().

public class TestClass {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        TestClass test= mapper.readValue(
                "{\r\n" + 
                "    \"response\": {\r\n" + 
                "        \"id\": 157016,\r\n" + 
                "        \"status\": {\r\n" + 
                "            \"long\": \"Long Value\",\r\n" + 
                "            \"short\": \"LV\",\r\n" + 
                "            \"elapsed\": 20\r\n" + 
                "        }\r\n" + 
                "    }\r\n" + 
                "}",TestClass.class); // <-- here, it's not Response, but an outer class
        System.out.println(test.response.id);
        System.out.println(test.response.status._long);
    }
    public Response response;         // <-- containing "response"
    static public class Response {
        public long id;
        public Status status;
    }
    static public class Status {
        @JsonProperty("long")
        public String _long;
        @JsonProperty("short")
        public String _short;
        @JsonProperty("elapsed")
        public Object elapsed;
    }
}

Displays

157016
Long Value

as expected. Kept only id from the outer object for the sake of brevity.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 dushkin
Solution 2
Solution 3 slindenau
Solution 4 tevemadar