98

I've got an application that stores some data in DynamoDB using Jackson to marshall my complex object into a JSON.

For example the object I'm marshalling might look like this:

private String aString;
private List<SomeObject> someObjectList;

Where SomeObject might look like this:

private int anInteger;
private SomeOtherObject;

and SomeOtherObject might look like this:

private long aLong;
private float aFloat; 

This is fine an the object gets marshalled no problem and stored in the DB as a JSON string.

When it comes time to retrieve the data from DynamoDB Jackson automatically retrieves the JSON and converts it back... EXCEPT that 'someObjectList' is returned as a List<LinkedHashMap> not as a List<SomeObject>! This is standard behaviour for Jackson, its not a mistake that this is happening.

So now this leads to a problem. My code base thinks its dealing with a List<SomeObject> but the reality is that its handling a List<LinkedHashMap>! My question is how do I get my LinkedHashMap back into a 'SomeObject'. Obviously this is a manual process but what I mean is I can't even extract the values.

If I do this:

for (LinkedHashMap lhm : someObjectList) {
    // Convert the values back
}

I get a compile error telling me that someObjectList is of type 'SomeObject' not LinkedHashMap.

If I do this:

for (SomeObject lhm : someObjectList) {
    // Convert the values back
}

I get a runtime error telling me that LinkedHashMap cannot be cast to 'SomeObject'.

Sam Berry
  • 7,394
  • 6
  • 40
  • 58
tarka
  • 5,289
  • 10
  • 51
  • 75

6 Answers6

238

You can use ObjectMapper.convertValue(), either value by value or even for the whole list. But you need to know the type to convert to:

POJO pojo = mapper.convertValue(singleObject, POJO.class);
// or:
List<POJO> pojos = mapper.convertValue(listOfObjects, new TypeReference<List<POJO>>() { });

this is functionally same as if you did:

byte[] json = mapper.writeValueAsBytes(singleObject);
POJO pojo = mapper.readValue(json, POJO.class);

but avoids actual serialization of data as JSON, instead using an in-memory event sequence as the intermediate step.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • But, the second example does not require a static reference to the POJO type. – Bo Søborg Petersen Feb 26 '16 at 13:42
  • Awesome. The only thing that I had to do is to make sure that the POJO class contain a default constructor (in case other parameterized constructors are present) – Gnana Mar 03 '16 at 22:33
  • I mean that with the first example the type has to be known at design time. You cannot just pass the type of the object to the (de)serializing method, which is a problem if you are making one method to (de)serialize any kind of class. With the second example you can just use .getClass() for serialization and pass in the type for deserialization. – Bo Søborg Petersen Apr 12 '16 at 15:23
  • @BoSøborgPetersen Ok, that is true with `TypeReference`, but you can construct `List` type with `TypeFactory` (`constructCollectionType(elementType)` or such), not a general limitation. – StaxMan Apr 14 '16 at 04:08
  • What about `TypeReference ref = new TypeReference() { }; mapper.readValue(source, ref);` where I pass in a generic type? – Bron Davies Apr 27 '16 at 05:00
  • @BronDavies type variables are compile-time things so that is no different from `new TypeReference() { }` -- there is no `T` during runtime; type binding only exists during compilation for this usage. There are places where variables do exist (class, method, field definitions), but not in actual program code. – StaxMan Apr 29 '16 at 21:08
  • 5
    Just a quick note for new readers: The TypeReference class in the code snippet comes from "com.fasterxml.jackson.core.type.TypeReference" – AliReza Jan 17 '20 at 19:59
  • Thank you so much, it helps me a lot! i just have to use mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); for some unmapped attributes. – Yuri Jun 16 '20 at 15:39
  • Make sure also that the `POJO` class has a no-argument constructor so that Jackson can instantiate it, and also that any attribute or field in the `POJO` is marked `@JsonProperty` – Jose Mhlanga Aug 03 '20 at 10:17
12

I had similar Issue where we have GenericResponse object containing list of values

 ResponseEntity<ResponseDTO> responseEntity = restTemplate.exchange(
                redisMatchedDriverUrl,
                HttpMethod.POST,
                requestEntity,
                ResponseDTO.class
        );

Usage of objectMapper helped in converting LinkedHashMap into respective DTO objects

 ObjectMapper mapper = new ObjectMapper();

 List<DriverLocationDTO> driverlocationsList = mapper.convertValue(responseDTO.getData(), new TypeReference<List<DriverLocationDTO>>() { });
Jared Forth
  • 1,577
  • 6
  • 17
  • 32
Aravind Rapuru
  • 161
  • 1
  • 3
3
public static <T> List<T> getObjectList(final String json, final Class<T> cls) 
{
    return objectMapper
        .readValue(json, objectMapper.getTypeFactory()
        .constructCollectionType(ArrayList.class, cls));
}
Chris
  • 26,361
  • 5
  • 21
  • 42
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 07 '22 at 06:43
  • This solution really helped me generalize my API calls that have to return lists of objects of my class type. – ychaulagain Dec 22 '22 at 22:15
0

There is a good solution to this issue:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper objectMapper = new ObjectMapper();

***DTO premierDriverInfoDTO = objectMapper.convertValue(jsonString, ***DTO.class); 

Map<String, String> map = objectMapper.convertValue(jsonString, Map.class);

Why did this issue occur? I guess you didn't specify the specific type when converting a string to the object, which is a class with a generic type, such as, User <T>.

Maybe there is another way to solve it, using Gson instead of ObjectMapper. (or see here Deserializing Generic Types with GSON)

Gson gson = new GsonBuilder().create();

Type type = new TypeToken<BaseResponseDTO<List<PaymentSummaryDTO>>>(){}.getType();

BaseResponseDTO<List<PaymentSummaryDTO>> results = gson.fromJson(jsonString, type);

BigDecimal revenue = results.getResult().get(0).getRevenue();
Donald Choi
  • 321
  • 4
  • 7
  • 2
    Please also provide an explanation :) – Mohit Motwani Mar 20 '19 at 09:36
  • While this code snippet may be the solution, [including an explanation](//meta.stackexchange.com/questions/114762/explaining-entirely-‌​code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Johan Mar 20 '19 at 09:37
  • @Johan OK, Thanks for the warning. I'm a novice for answering some question on Stack Overflow, so I'll do better in the future. – Donald Choi Apr 19 '19 at 07:12
0

I use com.fasterxml.jackson.databind.ObjectMapper to mapping from LinkedHashMap to Json string first and convert from json string to Object

Map<String, String> mappingValue
OBJECTA a = objectMapper.readValue(toJson(mappingValue), OBJECTA.class);

public static String toJson(Object object) {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    String jsonString = "";

    try {
        jsonString = mapper.writeValueAsString(object);
    } catch (JsonProcessingException var4) {
        var4.printStackTrace();
        jsonString = "Can't build json from object";
    }

    return jsonString;
}
vuhoanghiep1993
  • 715
  • 1
  • 8
  • 15
0

First you have to convert linkedhashmap to JsonString and then it can be converted into complex object.

Hari
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 02 '22 at 12:22