JEP 540: Simple JSON API (Now in Incubator)
Define a simple, standard API for parsing and generating JSON documents so that doing so does not require an external library. Enable many JSON processing tasks to be accomplished with little coding. This is an incubating API .
This JEP supersedes JEP 198 , Light-Weight JSON API , which was written in 2014. Circumstances have changed in the intervening years, so here we take a different approach.
Keep the API small, simple, and easy to learn. Provide only those data types and operations required for strict conformance to RFC 8259, in order to facilitate machine-to-machine communication. Avoid features such as multiple parsing configurations, syntax extensions, data binding, and streaming.
Ensure that code that navigates and extracts data from JSON documents with a known structure is simple and readable. Because JSON documents do not have schemas, such code serves as a de facto schema and should be readable as such.
Enable easy and quick exploration of unfamiliar JSON documents. We often interact with JSON documents in an exploratory manner, writing code not using a specification but instead trying it out against example documents. The API should provide methods that fail fast with clear error messages, enabling quick exploration.
Ensure that missing or unexpected values can be handled in a resilient fashion, since JSON document structures can evolve over time.
Make the JDK itself capable of parsing and generating JSON documents.
JSON is ubiquitous in modern computing. The Java ecosystem contains a wide range of established JSON libraries: Jackson , Gson , Jakarta JSON Processing and Binding , Fastjson 2 , and more. Not only do these libraries enable the parsing and generation of JSON documents, but they also support extended JSON syntaxes such as JSON5 and include higher-level features such as data binding, i.e., converting Java objects to and from JSON with a high degree of customization, and event-based streaming.
We often, however, just need to perform simple tasks such as extracting some data from a JSON document. The Python or Go code to accomplish such tasks is simple; the Java code should be equally simple.
For example, consider the task of computing the average of a set of forecast temperatures in a response from the U.S. National Weather Service REST API . The response is a JSON document that looks like this:
{ ... "properties": { ... "periods": [ { "number": 1, "name": "Today", "startTime": "2026-04-22T06:00:00-04:00", "endTime": "2026-04-22T18:00:00-04:00", "isDaytime": true, "temperature": 54, "temperatureUnit": "F", ... }, { "number": 2, "name": "Tonight", "startTime": "2026-04-22T18:00:00-04:00", "endTime": "2026-04-23T06:00:00-04:00", "isDaytime": false, "temperature": 48, "temperatureUnit": "F", ... }, { "number": 3, "name": "Thursday", "startTime": "2026-04-23T06:00:00-04:00", "endTime": "2026-04-23T18:00:00-04:00", "isDaytime": true, "temperature": 68, "temperatureUnit": "F", ... }, ... ] } } To compute the average forecast temperature requires parsing the document, navigating to the location in the structure that contains the forecasts, and iterating over the array of forecasts while extracting the temperature data. We should be able to tackle simple tasks like this with simple Java code, without installing an external library and without suspecting that another language might make us more productive.
A key goal driving the recent evolution of the Java Platform has been to enable simple tasks to be accomplished more easily and with less ceremony. Features serving this goal include convenience factory methods for collections , var declarations , running programs from source files , and compact source files and instance main methods . A simple JSON API for parsing and generating JSON documents would also serve this important goal.
A standard JSON API in the Java Platform would also pave the way for further use of JSON in the Platform and by the JDK itself, since the JDK cannot have external dependencies. One potential use case is configuration files. The JDK uses the property file format for various configuration files, such as security properties files . A weakness of this format is that it cannot express structured data. To represent an array in a property file, you must use clumsy workarounds such as sequentially numbered properties:
security.provider.1=SUN security.provider.2=SunRsaSign security.provider.3=SunEC ... With JSON built into the JDK, configuration files could represent arrays naturally, using JSON arrays:
{ "providers": [ "SUN", "SunRsaSign", "SunEC" ], ... } Description The jdk.incubator.json API is organized around the JsonValue interface, which represents a JSON value.
The JSON syntax has four kinds of primitives:
JSON strings, delimited with double quotes:
"Hello" "My name is 'Bob'" "\u006a\u0061\u0076\u0061" JSON numbers, represented in base 10 using decimal digits:
6 6.0 31.84 2.9E+5 JSON boolean literals: true and false
JSON objects, delimited by { } and composed of comma-separated members. A member has a name, also called a key, and a value, separated by a colon:
{ "address" : "123 Smith Street", "value" : 31.84, "coordinates" : [ [ 37, 23, 41 ], [ -121, 57, 10 ] ] } JSON arrays, delimited by [ ] and composed of comma-separated JSON values:
[ 1, 2, 3, { "value": "4" }, [ 5, 6 ] ] The JsonValue interface thus has six corresponding sub-interfaces: JsonString , JsonNumber , JsonBoolean , JsonNull , JsonObject , and JsonArray . Each interface declares operations appropriate to its corresponding JSON syntactic element: Instances of the primitive sub-interfaces offer conversions to Java primitives and strings, JsonObject instances expose members, and JsonArray instances expose array elements.
The JsonValue interface is sealed , which guarantees that any JsonValue instance is always one of this fixed set of subtypes and thus exhaustive switch expressions and statements do not require a default clause.
The JSON API makes it easy to parse JSON documents that conform to RFC 8259 . The parse method of the Json class returns a tree of JsonValue instances that expose the names, types, and values of the parsed JSON data. Returning to the National Weather Service example, we can compute the average forecast temperature in just a few lines:
String body = ... REST response body, which is a JSON document ... ; JsonValue json = Json.parse(body); json.get("properties").get("periods").asList().stream() .mapToInt(j -> j.get("temperature").asInt()) .average() .ifPresent(IO::println); (The complete example is shown in the Appendix .)
The API also makes it easy to generate JSON documents. For example, this code:
IO.println(JsonObject.of(Map.of("providers", JsonArray.of(List.of(JsonString.of("SUN"), JsonString.of("SunRsaSign"), JsonString.of("SunEC")))))); produces the output:
{"providers":["SUN","SunRsaSign","SunEC"]} Parsing and navigating JSON documents The Json class can parse a JSON document contained in either a String or a char array. A JSON document might be a REST API response body read from the network, a configuration file read from disk, or some other text payload produced by an application.
Parsing a JSON document requires a single call to one of the Json.parse methods:
JsonValue root = Json.parse(doc); Parsing is strict: The document must conform to RFC 8259 . Syntax extensions such as trailing commas and comments are not supported. Additionally, documents must not have objects with duplicate member names. This policy, permitted by the RFC, provides maximum interoperability and predictability, and reduces concerns about processing malformed or ambiguous JSON documents. (See below for a full discussion.)
Successful parsing returns an instance of JsonValue . Unsuccessful parsing throws an unchecked JsonParseException . The exception includes a detail message that provides specific information about the error and its location in the document. For example, the exception thrown when a document has duplicate member names in an object has the form:
jdk.incubator.json.JsonParseException: The duplicate member name: "foo" was already parsed. Location: line 42, position 69 Most JSON documents have a JSON object or JSON array at the root. For example, a JSON-formatted thread dump produced by the jcmd tool contains a root object:
{ "threadDump": { "formatVersion": 2, "processId": 45178, "time": "2026-04-16T23:13:02.709630Z", "runtimeVersion": "27-internal", "threadContainers": [ { "container": "<root>", "parent": null, "owner": null, "threads": [ { "tid": 3, "time": "2026-04-16T23:13:02.906891Z", "name": "main", "state": "WAITING", ... The root object contains a single member, the nested threadDump object, and threadDump itself contains both primitive and structural JSON values.
Once you have obtained the root JsonValue via Json.parse(...) , you can retrieve values from objects and arrays via their access methods , which return the requested member value or array element as a JsonValue . It is not necessary to downcast a JsonValue to JsonObject to access a member value, or to JsonArray to access an array element.
get(String) obtains the value of an object member. To obtain the thread dump object:
JsonValue threadDump = root.get("threadDump"); get(int) obtains an array element. To obtain the root thread container:
JsonValue firstContainer = threadDump.get("threadContainers").get(0); If the JsonValue instance is of the wrong type, or if the requested member or element does not exist, the access methods throw a JsonValueException .
You can convert a JSON value to a Java value by calling one of the conversion methods of the JsonValue interface. For a conversion to succeed, the JsonValue must be an instance of the appropriate subtype of JsonValue :
For example, you can retrieve the Java String value associated with the thread dump's "time" member:
JsonValue threadDumpTime = threadDump.get("time"); String time = threadDumpTime.asString(); You can convert the thread containers array into a List of JsonValue instances and process each instance:
threadDump.get("threadContainers").asList().forEach(jv -> ...); You can access the thread dump object as a Map to retrieve the number of members:
int count = threadDump.asMap().size(); You can navigate deeply into a JSON document, chaining access methods and converting to a Java value only at the end. To retrieve the thread identifier value of the first thread in the root thread container:
long tid = threadDump.get("threadContainers").get(0) .get("threads").get(0).get("tid").asLong(); The design of the conversion methods eliminates most instanceof checking and downcasting in cases where a specific JSON data type is expected in a document:
asString() converts a JsonString instance into a Java String with RFC 8259 JSON escape sequences translated to their corresponding characters.
asInt() converts a JsonNumber instance to a Java int if its numeric value can be represented exactly.
asLong() converts a JsonNumber instance to a Java long if its numeric value can be represented exactly.
asDouble() converts a JsonNumber instance to a Java double if its numeric value can be represented accurately.
asBoolean() converts a JsonBoolean instance to a Java boolean value of true or false .
asMap() converts a JsonObject instance into an unmodifiable Java Map . If the JSON object contains no members, an empty Map is returned.
asList() converts a JsonArray instance into an unmodifiable Java List . If the JSON array contains no elements, an empty List is returned.
There is no conversion method for the JSON null value. JsonNull instances can be handled by testing for instanceof JsonNull or via the tryValue method.
If a JsonValue is not an instance of the appropriate subtype for a conversion method then the method throws a JsonValueException . For example, calling asInt() on a JsonValue that is an instance of JsonString will always throw this exception. No attempt is made to parse the string
Hacker News
news.ycombinator.com