2

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.

dushkin
  • 1,939
  • 3
  • 37
  • 82
  • 1
    One question to avoid possible misunderstandings : `"status"` is one of the properties inside the `Response` json like `{"status": {}, ...}`? – dariosicily May 14 '22 at 16:42
  • @dariosicily I updated the question with an extended example. Thanks – dushkin May 14 '22 at 16:49
  • This is exactly what `@JsonProperty()` is meant for, and your example JSON+class parses properly. Probably it's not `Response.class` which you should provide in the `readValue()` call, but another class, which contains a `public Response response;` inside. – tevemadar May 26 '22 at 16:49

4 Answers4

2

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!

dushkin
  • 1,939
  • 3
  • 37
  • 82
1

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));
dariosicily
  • 4,239
  • 2
  • 11
  • 17
  • Dario, for me it is better actually to ignore it. I don't need it. If there is a way to ignore this part of string in json it would be the best for me. If possible, I can skip this string part. – dushkin May 14 '22 at 17:38
  • Ok, I know what I can do. Since I don;t care about these values, I'll just rename their names and adapt the class memebers name accordingly: – dushkin May 14 '22 at 17:41
  • @NotNull private static String mitigateLongAndShortValueNames(String resBody) { resBody = resBody.replaceAll("\"long\":", "\"longValue\":"); resBody = resBody.replaceAll("\"short\":", "\"shortValue\":"); return resBody; } – dushkin May 14 '22 at 17:46
0

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

slindenau
  • 1,091
  • 2
  • 11
  • 18
0

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.

tevemadar
  • 12,389
  • 3
  • 21
  • 49