Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Understanding TypeReference in Java for Converting JSON to Map means preserving generic target information such as Map<String, Object> during Jackson deserialization. Use mapper.readValue(json, new TypeReference<Map<String, Object>>() {}) because Map.class cannot express the map’s key and value types.

TypeReference<T> does not parse JSON itself. Jackson’s ObjectMapper performs the conversion, while TypeReference describes the complete destination type. The distinction matters for nested maps, lists, domain objects, numeric values, and any other parameterized Java type.

Key takeaways

  • TypeReference<Map<String, Object>> supplies Jackson with generic key and value information that Map.class cannot retain.
  • The empty braces in new TypeReference<...>() {} create an anonymous subclass that captures the parameterized type through its superclass metadata.
  • Map<String, Object> is appropriate for genuinely dynamic JSON, but a Java record or class is usually safer when the schema is known.
  • A JSON object should be deserialized into a map, while a root JSON array requires a target such as List<Integer> or List<Map<String, Object>>.
  • Use Jackson’s JavaType when generic types are assembled dynamically, and use JsonNode when the application needs to inspect irregular JSON structure.

What is TypeReference in Java for converting JSON to Map?

com.fasterxml.jackson.core.type.TypeReference is a Jackson class that preserves a complete generic target type for an ObjectMapper. For example, new TypeReference<Map<String, Object>>() {} tells Jackson to deserialize a JSON object as a map with string keys and flexible Java values.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Jackson’s ObjectMapper provides readValue overloads for a plain Class<T>, a Jackson JavaType, and a TypeReference<T>. The ObjectMapper API documentation describes the TypeReference overload as the option for types whose generic information cannot be represented by one Class object.

How do you convert JSON to Map with Jackson?

Pass the JSON text and an anonymous TypeReference containing the exact map type to ObjectMapper.readValue:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Map;

ObjectMapper mapper = new ObjectMapper();

String json = """
    {
      "name": "Ada",
      "age": 36,
      "active": true
    }
    """;

Map<String, Object> result = mapper.readValue(
    json,
    new TypeReference<Map<String, Object>>() {}
);

System.out.println(result.get("name"));   // Ada
System.out.println(result.get("age"));    // a Java Number

The target type describes the root JSON shape. The root value in this example is a JSON object, so a map is appropriate. The Object value type allows strings, numbers, booleans, lists, nested maps, and null values.

Why is Map.class not enough?

Map.class identifies only the raw map class; it does not communicate that the desired type is specifically Map<String, Object>, Map<String, Person>, or another parameterized map.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Java generics use type erasure. At runtime, the JVM generally retains the raw Map class but does not retain the String and Object arguments in a way that Map.class can convey to Jackson.

// The generic target is not represented by Map.class.
Map<String, Object> data = mapper.readValue(json, Map.class);

// The complete generic target is supplied explicitly.
Map<String, Object> typedData = mapper.readValue(
    json,
    new TypeReference<Map<String, Object>>() {}
);

The first form may compile with an unchecked-conversion warning, but the important limitation is not merely the warning. Jackson has not received the intended generic key and value metadata. Map.class can be acceptable when losing generic information is intentional:

Map<?, ?> data = mapper.readValue(json, Map.class);

When the map’s generic types matter, use TypeReference or JavaType.

What dependency provides TypeReference and ObjectMapper?

The jackson-databind dependency provides ObjectMapper and depends on Jackson Core and Jackson Annotations. The Maven coordinates are:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>

For Gradle:

implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")

TypeReference is not part of the Java standard library. Import it with:

import com.fasterxml.jackson.core.type.TypeReference;

Do not copy an old tutorial’s hard-coded version without checking the version approved by the application or organization. The Jackson Databind artifact page on Maven Central provides dependency metadata, while the project’s dependency management remains the appropriate source for the version actually used by a build.

To inspect the resolved Maven version, run:

mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind

For Gradle, run:

./gradlew dependencies --configuration runtimeClasspath

These commands show the version resolved by a particular project; they do not establish a universal current Jackson version.

What does the complete TypeReference example look like?

The following example converts strings, a number, a boolean, an array, and a nested object from one JSON document:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;
import java.util.Map;

public class JsonMapExample {
    public static void main(String[] args) throws JsonProcessingException {
        String json = """
            {
              "name": "Ada",
              "age": 36,
              "roles": ["developer", "author"],
              "address": {
                "city": "London"
              }
            }
            """;

        ObjectMapper mapper = new ObjectMapper();

        Map<String, Object> data = mapper.readValue(
            json,
            new TypeReference<Map<String, Object>>() {}
        );

        String name = (String) data.get("name");
        Number age = (Number) data.get("age");

        @SuppressWarnings("unchecked")
        List<String> roles = (List<String>) data.get("roles");

        @SuppressWarnings("unchecked")
        Map<String, Object> address =
            (Map<String, Object>) data.get("address");

        System.out.println(name);
        System.out.println(age);
        System.out.println(roles);
        System.out.println(address.get("city"));
    }
}

The top-level map declaration does not make nested values statically typed. A value retrieved through data.get has the declared type Object, so nested lists and maps still require careful casts or a more specific target model.

What do JSON values become in Map?

Jackson maps JSON values to general-purpose Java representations. The exact numeric class can depend on the value and mapper configuration, so application code should avoid assuming that every integer is an Integer.

JSON value Typical Java representation Important qualification
Object Map The concrete map implementation is an implementation detail.
Array List Elements use their inferred or declared target types.
String String Use a typed model when the string has domain meaning.
Boolean Boolean The value is either true or false.
Integer number Usually an integral Number, such as Integer or Long The selected class depends on the value and configuration.
Decimal number Usually Double by default Use an explicit decimal target such as BigDecimal when precision matters.
null null Check for null before casting or calling methods.

Read flexible numeric values as Number when the exact representation is not part of the contract:

Number amount = (Number) data.get("amount");
long wholeAmount = amount.longValue();

For financial or exact-decimal data, do not use double merely because the JSON contains a decimal number. Deserialize into an explicit decimal type or configure the mapper for the required numeric representation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should you use Map or Map?

Use Map<String, String> only when every value in the JSON object is expected to be a string. Use Map<String, Object> when values can legitimately have different JSON types.

Target type Use it when Example compatible shape
Map<String, String> Every value is a string. {"firstName":"Ada","country":"UK"}
Map<String, Object> The object is dynamic or contains mixed values. {"name":"Ada","age":36,"active":true}
Map<String, Person> Each map value follows a known person schema. {"author":{"name":"Ada","age":36}}
A Java record or class The complete object schema is known and used by business logic. {"name":"Ada","age":36,"active":true}

This strict target is suitable for all-string input:

Map<String, String> values = mapper.readValue(
    json,
    new TypeReference<Map<String, String>>() {}
);

The same target is not an appropriate representation for a document containing numbers, booleans, arrays, or nested objects. A flexible map accepts those values, but it sacrifices compile-time guarantees, validation, discoverability, and refactor safety.

For stable data, prefer a record or class:

record Person(String name, int age, boolean active) {}

Person person = mapper.readValue(json, Person.class);

How do nested maps, lists, and domain objects work?

TypeReference can describe nested generic structures as long as the target type matches the JSON shape.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Map of maps

Map<String, Map<String, Integer>> nested = mapper.readValue(
    json,
    new TypeReference<Map<String, Map<String, Integer>>>() {}
);

Map of lists

Map<String, List<String>> grouped = mapper.readValue(
    json,
    new TypeReference<Map<String, List<String>>>() {}
);

List of maps

List<Map<String, Object>> records = mapper.readValue(
    json,
    new TypeReference<List<Map<String, Object>>>() {}
);

Map containing domain objects

record Product(String name, double price) {}

Map<String, Product> products = mapper.readValue(
    json,
    new TypeReference<Map<String, Product>>() {}
);

A JSON object at the root can target a map, and a JSON array at the root can target a list. A JSON array cannot be deserialized into a map merely by changing the generic arguments.

JSON root Correct target example
{"a":1,"b":2} Map<String, Integer>
[{"id":1},{"id":2}] List<Map<String, Integer>>

Why are the empty braces after TypeReference important?

The braces in new TypeReference<Map<String, Object>>() {} create an ordinary Java anonymous subclass. The anonymous subclass allows Jackson’s TypeReference implementation to inspect the generic type declared by the subclass and capture the parameterized type.

// Correct: creates an anonymous subclass.
new TypeReference<Map<String, Object>>() {}

// Incorrect: TypeReference is abstract and cannot normally be instantiated directly.
new TypeReference<Map<String, Object>>()

The braces are not a JSON option and are not a special parsing flag. They are standard Java anonymous-class syntax. The technique preserves reflective type metadata for Jackson even though Java generics themselves are erased from ordinary runtime class information.

Can a TypeReference be reused?

A type reference can be stored as a constant when the same target type is used repeatedly:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
private static final TypeReference<Map<String, Object>> MAP_TYPE =
    new TypeReference<>() {};

Map<String, Object> data = mapper.readValue(json, MAP_TYPE);

The diamond operator works when the surrounding declaration provides enough information for Java to infer the generic type. Explicit generic arguments can be clearer in public APIs and examples.

How should a generic JSON helper method be written?

A generic helper should receive a concrete TypeReference<T> from its caller rather than trying to capture an unresolved method type variable inside a new anonymous type reference.

Use this pattern:

public static <T> T fromJson(
        ObjectMapper mapper,
        String json,
        TypeReference<T> type
) throws java.io.IOException {
    return mapper.readValue(json, type);
}

Map<String, Object> map = fromJson(
    mapper,
    json,
    new TypeReference<Map<String, Object>>() {}
);

List<Person> people = fromJson(
    mapper,
    peopleJson,
    new TypeReference<List<Person>>() {}
);

A tempting but unsafe design is:

public static <T> List<T> parse(String json) throws java.io.IOException {
    return mapper.readValue(json, new TypeReference<List<T>>() {});
}

The method type variable T may not be available as a concrete runtime type when the anonymous subclass is created. Pass an explicit TypeReference<T>, or construct a Jackson JavaType from a concrete class.

When should you use JavaType instead of TypeReference?

Use TypeReference for a statically known generic type and use JavaType when code must construct the target type programmatically.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import com.fasterxml.jackson.databind.JavaType;

JavaType mapType = mapper.getTypeFactory()
    .constructMapType(Map.class, String.class, Object.class);

Map<String, Object> data = mapper.readValue(json, mapType);

For a collection of domain objects:

JavaType listType = mapper.getTypeFactory()
    .constructCollectionType(List.class, Person.class);

List<Person> people = mapper.readValue(peopleJson, listType);
Situation Recommended approach Reason
Known static type such as Map<String, Person> TypeReference Compact and easy to read.
Key or value class is selected dynamically JavaType The type can be assembled from runtime class objects.
A reusable framework constructs multiple nested types JavaType Jackson’s TypeFactory provides programmatic construction.
Simple nested type known in source code TypeReference The complete generic declaration is visible at the call site.

Jackson documents both TypeReference and JavaType as supported readValue targets in the ObjectMapper.readValue API.

Should you use Map or JsonNode instead?

Use Map<String, JsonNode> when the document is an object but each field should remain in Jackson’s tree model. Use a root JsonNode when the JSON shape is highly irregular or the application needs to inspect nodes before deciding how to interpret them.

import com.fasterxml.jackson.databind.JsonNode;

Map<String, JsonNode> fields = mapper.readValue(
    json,
    new TypeReference<Map<String, JsonNode>>() {}
);

JsonNode root = mapper.readTree(json);
JsonNode name = root.path("name");

Map<String, Object> converts values immediately into ordinary Java values. JsonNode retains a richer JSON tree and can avoid repeated unchecked casts when fields vary substantially.

What is the difference between readValue and convertValue?

readValue parses JSON text, bytes, or a stream into a Java target. convertValue converts an existing Java object or representation into another Java target without treating the source as JSON text.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
// JSON text to a map
Map<String, Object> fromJson = mapper.readValue(
    json,
    new TypeReference<Map<String, Object>>() {}
);

// Existing Java object to a map
Map<String, Object> fromObject = mapper.convertValue(
    person,
    new TypeReference<Map<String, Object>>() {}
);

Choose readValue when the input is serialized JSON. Choose convertValue when the input is already a Java object, map, or other in-memory representation.

How do you handle TypeReference deserialization errors?

Jackson can report malformed JSON, a root-shape mismatch, or a value that cannot be converted to the requested target. Catch the checked exception at an application boundary or declare IOException from a parsing method.

try {
    Map<String, Object> data = mapper.readValue(
        json,
        new TypeReference<Map<String, Object>>() {}
    );
} catch (JsonProcessingException e) {
    // Invalid JSON or a JSON-to-target mismatch.
}

A method that exposes the checked exception can be written as:

public Map<String, Object> parse(String json) throws java.io.IOException {
    return mapper.readValue(
        json,
        new TypeReference<Map<String, Object>>() {}
    );
}
Failure or surprise Likely cause Correction
Malformed JSON Invalid JSON syntax. Validate the source and report the parsing error without assuming the result is an empty map.
Map target rejects input The root JSON token is an array or another non-object shape. Use a list or the target matching the actual root shape.
Value conversion fails A JSON value cannot become the requested number, boolean, or domain type. Correct the JSON or use a target type that represents the data.
Unexpected class-property error A domain class has an unknown or incompatible property under the mapper’s configuration. Review the class, JSON contract, and configured unknown-property behavior.
Empty input behaves unexpectedly Null input and empty content are different cases and depend on input handling and mapper configuration. Test both cases explicitly and define the API’s expected behavior.

Jackson’s documentation notes that mapping exceptions can occur when the input structure does not match the expected result type. The ObjectMapper mapping API documentation is the appropriate reference for the exception behavior of the Jackson version in use.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Root array versus root object

This input is an array:

String json = "[1, 2, 3]";

A map target is wrong because the root token is not an object:

Map<String, Object> result = mapper.readValue(
    json,
    new TypeReference<Map<String, Object>>() {}
);

Use a list target instead:

List<Integer> result = mapper.readValue(
    json,
    new TypeReference<List<Integer>>() {}
);

Missing fields versus explicit null

A missing property and a property explicitly set to JSON null can both produce data.get("field") == null. Use containsKey when the distinction matters:

boolean present = data.containsKey("field");
Object value = data.get("field");

Do not cast a null value or call a method on it without checking:

if (value != null) {
    System.out.println(value);
}

Which map key type should JSON objects use?

Ordinary JSON object member names are strings, so Map<String, ...> is the natural Java representation for a JSON object. A Java map with integer, enum, or another non-string key type requires explicit handling and should not be assumed to be the same as ordinary JSON object keys.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Map<String, Object> ordinaryJsonObject = mapper.readValue(
    json,
    new TypeReference<Map<String, Object>>() {}
);

How does TypeReference compare with Gson and Moshi?

Jackson, Gson, and Moshi all support typed handling of generic JSON structures, but the APIs differ.

Best Value
Sale
NLP: The Essential Guide to Neuro-Linguistic Programming
  • NLP: The Essential Guide to Neuro-Linguistic Programming
Library Generic map approach Practical distinction
Jackson TypeReference<Map<String, Person>> or JavaType JavaType is useful when nested types are assembled dynamically.
Gson TypeToken<Map<String, Person>> Gson’s official guide documents the anonymous TypeToken idiom for parameterized collections and maps.
Moshi Typed Java Map, List, and model types with Moshi adapters Moshi’s project documentation describes built-in handling for types such as List and Map and fewer configuration features than Gson.

Gson’s official user guide explains how an anonymous TypeToken retains a fully parameterized type. Gson’s troubleshooting documentation also covers problems caused by raw types and unresolved type variables. The Moshi project documentation describes Moshi’s built-in collection handling and its configuration differences from Gson.

The conceptual lesson is shared: a raw collection class cannot express all of the generic information required for reliable deserialization. The concrete type-capture API depends on the JSON library.

What should production code do differently?

Use a map for flexible data, but prefer a record or class for a stable business schema. A typed model makes fields discoverable, supports stronger validation, reduces unchecked casts, and makes refactoring safer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create and configure an ObjectMapper once and reuse it where practical rather than constructing a new mapper for every parse. The mapper should be deliberately configured for the application’s modules, naming rules, date formats, numeric requirements, and unknown-property policy.

Keep precision requirements explicit. Read flexible values as Number when the numeric class is not part of the contract, and use BigDecimal or another explicit target when decimal precision matters.

Do not enable polymorphic or default-typing features casually for untrusted JSON. A plain Map<String, Object> conversion does not require those features. Production applications should deliberately constrain allowed types, configure deserialization for the input trust boundary, and keep Jackson dependencies patched.

Log parsing failures carefully. JSON may contain credentials, tokens, personal data, or other sensitive fields, so logging the complete input can create a separate data-exposure problem.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which Jackson target should you choose?

Situation Recommended target Why
The schema is known Java class or record Provides typed fields, validation opportunities, and safer refactoring.
The JSON object is genuinely arbitrary Map<String, Object> Supports mixed and changing values.
Map values have a known schema Map<String, MyType> Preserves typed domain objects inside the map.
The nested generic type is determined dynamically Jackson JavaType Builds the destination type programmatically.
The application needs to inspect irregular JSON JsonNode Retains a navigable JSON tree and defers interpretation.
The application uses Gson TypeToken<Map<...>> Gson’s type-capture mechanism represents the parameterized map.
The application uses Moshi Typed Map, model, and Moshi adapter Uses Moshi’s built-in collection and adapter approach.

What is the final TypeReference rule?

Use new TypeReference<Map<String, Object>>() {} when Jackson must deserialize a JSON object into a map whose generic type matters. Use a more specific map or domain type whenever the JSON schema allows it, use JavaType for dynamically constructed generic types, and use JsonNode for irregular tree-shaped data.

Frequently Asked Questions

Is TypeReference required every time Jackson deserializes a Map?

No. Jackson can deserialize map-shaped JSON with Map.class, but Map.class does not preserve the desired generic key and value types. Use TypeReference when those types matter, such as Map<String, Person>.

Why does TypeReference use empty braces?

The empty braces create an anonymous Java subclass. Jackson inspects that subclass’s generic superclass metadata to capture the parameterized type, so new TypeReference<Map<String, Object>>() {} is valid while direct instantiation without the braces is not.

Can Map safely represent any JSON?

Map<String, Object> can represent a JSON object with mixed values, but it does not make nested values statically typed. Use JsonNode for highly irregular JSON or a record, class, or typed nested map when the schema is known.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I use for a JSON array instead of a Map?

Use a list target when the JSON root is an array. For example, [1, 2, 3] should be deserialized with new TypeReference<List<Integer>>() {}, not a map target.

The Bottom Line

Bottom line: TypeReference solves the gap between Java’s erased generic types and Jackson’s need to know the complete deserialization target. For a dynamic JSON object, use Map<String, Object>; for stable data, prefer a typed record or class; for runtime-built generic types, use JavaType.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.