The Paradigm Shift in Jackson 3

In Jackson 2, the streaming API (jackson-core) was somewhat tightly coupled to the object-mapping API (jackson-databind) via the ObjectCodec class. To resolve this, Jackson 3 introduces a cleaner separation of concerns by routing parser and generator creation through ObjectReadContext and ObjectWriteContext.

If you are migrating to Jackson 3 and want to stick strictly to jackson-core without pulling in jackson-databind, you might find yourself confused by the new JsonFactory method signatures, which now require an ObjectReadContext.

The Solution: Use ObjectReadContext.empty()

To create a JsonParser without jackson-databind, you do not need to implement your own context. Jackson 3 provides a built-in, lightweight, empty context specifically for core-only streaming operations: ObjectReadContext.empty().

Here is a complete, runnable example demonstrating how to parse JSON using only Jackson 3 Core:

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectReadContext;

import java.io.IOException;

public class Jackson3CoreExample {
    public static void main(String[] args) throws IOException {
        String json = "{\"name\": \"Jackson 3\", \"version\": 3}";

        // 1. Create the JsonFactory
        JsonFactory factory = new JsonFactory();

        // 2. Get the empty read context for core-only usage
        ObjectReadContext readContext = ObjectReadContext.empty();

        // 3. Create the JsonParser by passing the context and the source
        try (JsonParser parser = factory.createParser(readContext, json)) {
            while (parser.nextToken() != null) {
                JsonToken token = parser.currentToken();
                System.out.println("Token: " + token 
                    + " | Name: " + parser.currentName() 
                    + " | Value: " + parser.getValueAsString());
            }
        }
    }
}

Why This Change Was Made

In Jackson 2.x, the JsonParser held a direct reference to an ObjectCodec (usually the ObjectMapper). This forced a dependency cycle and meant the low-level parser had to know about high-level data-binding concepts.

In Jackson 3.x, this dependency is inverted. The parser only knows about the ObjectReadContext interface. When you use the full jackson-databind library, the DeserializationContext implements this interface. When you use only jackson-core, ObjectReadContext.empty() provides a no-op implementation, keeping your classpath lightweight and free of databind overhead.