You should create specific Map type and provide it into deserialization process:

TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Theme.class);
HashMap<String, Theme> map = mapper.readValue(json, mapType);
Answer from Michał Ziober on Stack Overflow
🌐
Baeldung
baeldung.com › home › json › jackson › map serialization and deserialization with jackson
Map Serialization and Deserialization with Jackson | Baeldung
January 8, 2024 - Here we’re telling Jackson to deserialize the Map<MyPair, String> contained in ClassWithAMap, so we need to extend KeyDeserializer to describe how to deserialize the map’s key, a MyPair object, from an input String:
Discussions

java - How to Deserialize a Map<String,Object> correctly in Jackson - Stack Overflow
I have the following POJO that can be serialized into bytes or json. public final class Message { private final Data data; private final Request request; private final Response response; ... More on stackoverflow.com
🌐 stackoverflow.com
June 24, 2021
java - Deserialize to a Map<String, Object> with correct type for each value - Stack Overflow
For example, in the sample above, ... instead deserialized as a String. I've seen examples for basing what type on a variety of things, but in my scenario, I won't know what the types are (they will be user generated objects that I won't know in advance), so I need to be able to serialize the type information with the key value pair. How can I accomplish this with Jackson... More on stackoverflow.com
🌐 stackoverflow.com
apex - Deserialize Very Specific JSON into Map <String, Object> - Salesforce Stack Exchange
Into a Map. Something simple like: More on salesforce.stackexchange.com
🌐 salesforce.stackexchange.com
March 2, 2023
java - Deserialize into Map<String, String> with Jackson - Stack Overflow
I have to agree with @Fortega that it sort of does, take a look at "3. Custom Deserializer on ObjectMapper" of that article 2018-01-02T11:58:17.12Z+00:00 More on stackoverflow.com
🌐 stackoverflow.com
February 1, 2018
🌐
GitHub
github.com › FasterXML › jackson-databind › discussions › 4341
Deserializing into Map<String, Object> based on a map-global type property · FasterXML/jackson-databind · Discussion #4341
This type defines all possible Map entries and their types. ... The serialization part is trivial, as the Java types are known. However, after trying for very long already, I simply cannot get the deserialization part working: I could write a Deserializer for the complete JSON. Then, however, I cannot use all the nice Jackson features for other properties.
Top answer
1 of 3
2

The solution that worked for me was using custom deserialization, @JsonDeserialize annotation & JsonDeserializer interface, in order to achieve the desired results.

Below is the solution:

public class Request {

  private String method;
  private String resource;
  @JsonDeserialize(using = BodyDeserializer.class)
  private final Map<String, Object> body;
  private final Map<String, String> headers;
  private final Map<String, String[]> parameters;

  public Request() {
    this.body = new HashMap();
    this.headers = new HashMap();
    this.parameters = new HashMap();
  }

  public String getMethod() {
    return method;
  }

  public String getResource() {
    return resource;
  }

  public Map<String, Object> getBody() {
    return body;
  }

  public Map<String, String> getHeaders() {
    return headers;
  }

  public Map<String, String[]> getParameters() {
    return parameters;
  }

  public String getHeader(String name) {
    return headers.get(name);
  }

  public Request setBody(Map<String, Object> body) {
    this.body.putAll(body);
    return this;
  }

  public Request setMethod(String name) {
    this.method = name;
    return this;
  }

  public Request setResource(String name) {
    this.resource = name;
    return this;
  }

  public Request setHeaders(Map<String, String> headers) {
    this.headers.putAll(headers);
    return this;
  }

  public Request setParameters(Map<String, String[]> parameters) {
    this.parameters.putAll(parameters);
    return this;
  }

  private static class BodyDeserializer extends JsonDeserializer<Map<String, Object>> {

    @Override
    public Map<String, Object> deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
      JsonDeserializer<Object> deserializer = dc.findRootValueDeserializer(dc.constructType(Map.class));
      Map<String, Object> map = (Map<String, Object>) deserializer.deserialize(jp, dc);
      return map;
    }
  }
}
2 of 3
1

For the String-Problem, these sources might help:

Can not deserialize instance of java.lang.String out of START_OBJECT token

https://www.baeldung.com/jackson-map#1-mapltstring-stringgt-deserialization

Why this code can't work

Jackson is not much more powerful than you are.

If Jackson gets an object to serialize, it tries to serialize all of its values. And only its values (which is pretty good for the independence from classes). This is a json object:

{
"type":"apple",
"quantity":3,
"imageID":17
}

Now, what is the class of this object? It could be Fruit.class, Image.class or even RoundObject.class, json doesn't know and Jackson neither.

So how does json find out what the class is? By looking at the type of the object reference. In your case, it's Object. In Object.class, Jackson cannot find a constructor that requires the variables of the object that has been saved, so it crashes.

Solution

Trying to serialize objects is not a good idea. If you have very different classes you want to put in, e.g. Apple and Banana, make an interface or abstract class called Fruit that both of them implement. Now, use this annotation at the top of this class:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type") // name of the variable to save the kind of object you put in. NO VARIABLES in all classes that extend from Fruit are allowed to have this name (or at least @JsonProperty).
@JsonSubTypes({
        @JsonSubTypes.Type(value = Apple.class, name = "banana"),
        @JsonSubTypes.Type(value = Banana.class, name = "apple"),
})

And using a Map<String, Fruit> should work.

Top answer
1 of 1
24

I think the simplest way of achieve what you want is using:

ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

This will add type information in the serialized json.

Here you are a running example, that you will need to adapt to Spring:

public class Main {
    
    public enum MyEnum {
        enumValue1
    }
        
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
            
        MyClass obj = new MyClass();
        obj.setContext(new HashMap<String, Object>());
            
        obj.setVal1("foo");
        obj.setVal2("var");
        obj.getContext().put("key1", "stringValue1");
        obj.getContext().put("key2", MyEnum.enumValue1);
        obj.getContext().put("key3", 3.0);

        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            
        System.out.println(json);
            
        MyClass readValue = mapper.readValue(json, MyClass.class);
        //Check the enum value was correctly deserialized
        Assert.assertEquals(readValue.getContext().get("key2"), MyEnum.enumValue1);
    }
}

The object will be serialized into something similar to:

[ 
  "so_27871226.MyClass", 
  {
    "val1" : "foo",
    "val2" : "var",
    "context" : [ 
      "java.util.HashMap", 
      {
        "key3" : 3.0,
        "key2" : [ "so_27871226.Main$MyEnum", "enumValue1" ],
        "key1" : "stringValue1"
      } 
    ]
  } 
]

And will be deserialized back correctly, and the assertion will pass.

By the way, there are more ways of doing this, please look at https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization for more info.

I hope it will help.

🌐
Makeinjava
makeinjava.com › home › convert map to/from json string in java (jackson objectmapper/ example)
Convert Map to/from JSON string in java (jackson objectmapper example)
January 1, 2024 - Given Map of String and object, serialize map of String and user defined objects or POJOs to corresponding JSON and vice versa using Jackson ObjectMapper
Find elsewhere
🌐
Javatpoint
javatpoint.com › map-serialization-and-deserialization-with-jackson
Map Serialization and Deserialization with Jackson - javatpoint
s JSON Views is another important element of Jackson. JSON Views is also used for serializing and deserializing Java objects. We use @JsonView annotation for customizing the view at the time of serializing/deserializing.
🌐
QA Automation Expert
qaautomation.expert › 2021 › 11 › 26 › deserialization-how-to-convert-json-to-map-using-jackson-api
Deserialization – How to convert JSON to Map using Jackson API
November 26, 2021 - HOME This tutorial shows how to convert JSON to JAVA map using Jackson’s data binding. In the previous tutorials, I have explained converting Java Objects/Arrays to JSON String using Jackson …
🌐
GitHub
github.com › FasterXML › jackson-databind › discussions › 4330
handling Map<String,Object> of known types but not polymorphic · FasterXML/jackson-databind · Discussion #4330
I've used defaultTyping(...) with a TypeResolverBuilder and it serialises type info into the values where needed. This deserialises properly too. But this approach has an effect on any generic object, and so other instances of Map<String,Object> fail to deserialise.
🌐
GitHub
github.com › vavr-io › vavr-jackson › issues › 138
Bug: Cannot deserialize to Map<String, String> · Issue #138 · vavr-io/vavr-jackson
September 27, 2019 - to a class that has the property io.vavr.collection.Map<String, String> test; I get following exception: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of VALUE_NULL token. With an Java Map everything works fine. I'm using Vavr 0.10.0 and Jackson 2.9.9.1 · Example: import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import io.vavr.jackson.datatype.VavrMo
Author: vavr-io
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Deserialize to a Map with Correct Type Example - Java Code Geeks
October 7, 2025 - Line 12: defines mapTypeRef for Map<String,Object>. Line 21,24,26: implements deserializeString by calling xmlMapper.readValue to get the Map. But the Map's value is type of String as XML stores everything as String, calling MapTypeConverter created at step 2.2 to convert to the correct data type. In this step, I will create a DeserializeMapObjectMapperImpl class that implements DeserializeMapService interface defined at step 3.1 via com.fasterxml.jackson.databind.ObjectMapper.
🌐
Stack Abuse
stackabuse.com › convert-json-string-to-java-map-with-jackson
Convert JSON String to Java Map with Jackson
March 16, 2023 - String json = "{\n" + "\"Task 1\" : \"In_Progress\",\n" + "\"Task 2\" : \"Done\",\n" + "\"Task 3\" : \"Planned\"\n" + "}"; // ObjectMapper instantiation ObjectMapper objectMapper = new ObjectMapper(); // Deserialization into a Map Map<String, TASK_STATUS> result = objectMapper.readValue(json, HashMap.class); // Printing the results System.out.println(result.entrySet()); We've chucked the json contents into the readValue() method, and since it contains JSON that can be deserialized into a Map, given the key-value pairings, told Jackson to deserialize into a HashMap.
🌐
Baeldung
baeldung.com › home › json › jackson › deserialize to a map with correct type for each value
Deserialize to a Map with Correct Type for Each Value | Baeldung
October 2, 2025 - In this example, we first create an ObjectMapper to handle the JSON processing. Then we use a TypeReference<Map<String, Object>> to tell Jackson that we want to deserialize the JSON into a map with string keys and values of different types.