JEP draft: Simple JSON API

AuthorsNaoto Sato, Paul Sandoz, Justin Lu, Stuart Marks
OwnerNaoto Sato
TypeFeature
ScopeJDK
StatusSubmitted
Componentcore-libs
Discussioncore dash libs dash dev at openjdk dot org
EffortM
DurationM
Reviewed byAlex Buckley
Created2024/11/13 23:54
Updated2026/04/29 17:36
Issue8344154

Summary

Provide a simple API to help Java developers read and write JSON documents without the overhead of installing an external library. Many useful JSON processing tasks can be accomplished with minimal coding. This is an incubating API.

History

This JEP supersedes JEP 198, Light-Weight JSON API. Circumstances have changed in the intervening years, and this JEP is pursuing a different approach.

Goals

Non-Goals

Motivation

JSON is ubiquitous in modern computing. Java developers can choose from a wide range of established JSON libraries: Jackson, Gson, Jakarta JSON Processing and Binding, FastJson2, and many more. These libraries provide not only parsing of JSON documents, but also higher level features such as data binding (converting Java objects to and from JSON with a high degree of customization), event-based streaming, and parsing of extended JSON syntax such as JSON5.

However, developers often need to perform simple tasks such as extracting and processing data from JSON documents. These tasks are simple in languages such as Python or Go. It should be equally simple to accomplish such tasks in Java. For example, consider the task of computing the average of a set of forecast temperatures extracted from a response to the 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",
          ...
        },
        ...
      ]
    }
  }

The task involves parsing the document, navigating to the location in the structure that contains the forecasts, and iterating the array of forecasts, while extracting the temperature data. (A complete example of this is shown in the Appendix.) It would be ideal if developers could tackle simple tasks like this in Java without installing an external library or feeling that another language would make them more productive.

Java has made great strides in handling simple tasks with short no-ceremony programs -- var declarations, convenience methods for collections, compact source files (no more public static void main), running programs from source (java Task.java), and so on. Offering a simple JSON API to extract data from JSON documents continues this trend.

JSON support in the JDK also paves the way for use of JSON by the JDK, because the JDK cannot have external dependencies. One potential use case is for configuration files. The JDK uses Properties format for several configuration files, such as java.security. A weakness of Properties format is that it has no support for storing structured data. For example, to represent an array in a Properties file, one can use a series of sequentially numbered properties -- a clumsy workaround at best:

security.provider.1=SUN
  security.provider.2=SunRsaSign
  security.provider.3=SunEC
  ...

With JSON built into the JDK, configuration files can represent arrays quite naturally using JSON arrays.

In addition, the JDK can produce diagnostic output in JSON. For example, the following jcmd command produces a thread dump in JSON format:

jcmd <pid> Thread.dump_to_file -format=json threaddump.out

A portion of the output file looks like this:

{
    "name": "workerThread",
    "tid": 28,
    "stack": [
      "java.base\/jdk.internal.vm.Continuation.yield(Continuation.java:388)",
      "java.base\/java.lang.VirtualThread.yieldContinuation(VirtualThread.java:352)",
      "java.base\/java.lang.VirtualThread.park(VirtualThread.java:515)"
    ]
  }

It is extremely useful to be able to process this data using only mechanisms built into the JDK.

Description

The jdk.incubator.json.Json class provides a simple parsing experience for in-memory JSON documents that conform to RFC 8259. It returns a tree of jdk.incubator.json.JsonValue instances that expose the names, types, and values of JSON data. Returning to the National Weather Service example from the Motivation section, the average temperature forecast can be parsed and computed in just a few lines:

String body = /* REST response body, 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.

Json and JsonValue design principles

Json and JsonValue are designed with the following principles:

  1. Make code that navigates known JSON structures simple and readable. JSON documents are schema-less. JsonValue provides methods that make it easy to write code to navigate and extract data from documents that have a known structure. Once written, the code becomes the de facto specification of the schema for those JSON documents and serves as a kind of conformance test for them. It is thus advantageous for the code that validates document structure to be simple and easy to read.

  2. Each kind of syntactic element in the JSON syntax should have a corresponding type in the API. JSON syntax has four primitives (strings, numbers, booleans, and null) and two structures (objects and arrays). The JsonValue interface has six sub-interfaces that correspond directly to these elements.

  3. Enable the quick exploration of unfamiliar JSON data. Developers often interact with JSON in an exploratory manner, writing code not using a specification but instead trying it out against example documents. JsonValue provides methods that fail fast with clear error messages, enabling quick exploration.

  4. Enable the handling of missing and unexpected values in a resilient fashion. JSON documents can change freely over time, or they can even have differing structure within the same document. JsonValue provides techniques to flexibly handle these situations.

JSON syntactic elements and Java types

A JSON document has six kinds of syntactic elements.

  1. A JSON string, delimited with double quotes:
"Hello"
  "My name is 'Bob'"
  "\u006a\u0061\u0076\u0061"
  1. A JSON number, represented in base 10 using decimal digits:
6  6.0  31.84  2.9E+5
  1. A JSON object, 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 ]
  }
  1. A JSON array, delimited by [ ] and composed of comma-separated JSON values:
[ 1, 2, 3, { "value": "4" }, [ 5, 6 ] ]
  1. The JSON boolean literals: true and false

  2. The JSON null literal: null

Each of the JSON syntactic elements has a corresponding Java subtype of the JsonValue interface. This interface is sealed so that it is not user extensible. User code can be assured that every JsonValue instance is always one of this fixed set of subtypes.

sealed interface JsonValue
    permits JsonString, JsonNumber, JsonObject, JsonArray, JsonBoolean, JsonNull {
        ...
    }

    interface JsonString  extends JsonValue { ... }
    interface JsonNumber  extends JsonValue { ... }
    interface JsonObject  extends JsonValue { ... }
    interface JsonArray   extends JsonValue { ... }
    interface JsonBoolean extends JsonValue { ... }
    interface JsonNull    extends JsonValue { ... }

These interfaces offer operations appropriate to their JSON types: for example, JsonObject values expose members, JsonArray values expose elements, and primitive values expose conversions to Java primitives and strings.

Parsing and navigating JSON documents

Json parses JSON documents as text in the form of either a String or char[].

For example, a JSON document might be a REST API response body read from network, a configuration file read from disk, or some other text payload produced by an application. Parsing a JSON document takes only one call to Json.parse():

JsonValue root = Json.parse(doc);

Parsing is strict: the document must conform to RFC 8259. Syntax extensions such as trailing commas or comments are not supported. In addition, documents must not have objects with duplicate member names. This policy, permitted by the RFC, provides maximum interoperability and predictability, and it reduces concerns about processing malformed or ambiguous JSON documents. See the Alternatives section for a full discussion.

Successful parsing returns an instance of JsonValue. Unsuccessful parsing throws an unchecked JsonParseException. This exception includes a detail message that provides specific information about the exact error and where it occurred in the document. For example, the exception thrown from failing to parse a document with duplicate member names in an object will appear similar to:

jdk.incubator.json.JsonParseException: The duplicate member name: "..." was
  already parsed. Location: line ..., position ...

Most JSON documents have a JSON object or JSON array at the root. For example, consider a JSON-formatted thread dump produced by jcmd which contains a root object:

{
    "threadDump": {
      "formatVersion": 2,
      "processId": 45178,
      "time": "2026-04-16T23:13:02.709630Z",
      "runtimeVersion": "27-internal",
      "threadContainers": [
        {
          "container": "<root>",
          ...

The root object contains a single member, the nested threadDump object. threadDump itself contains both primitive and structural JSON values.

Once you have obtained the root JsonValue with parse, you can retrieve values from objects and arrays by calling the following access methods. These methods return the requested member value or array element as a JsonValue. If the receiver is of the wrong kind, or if the requested member or element does not exist, they throw JsonValueException.

For example, to obtain the thread dump object, you can call:

JsonValue threadDump = root.get("threadDump");

For example, to obtain the root thread container, you can call:

JsonValue firstContainer = threadDump.get("threadContainers").get(0);

Converting JSON values to Java values

You can convert a JSON value to a particular Java type by calling one of the conversion methods that appear on the JsonValue interface. For a conversion method to succeed, the JsonValue must be an instance of the appropriate JsonValue subtype. The following table shows the subtype required for each conversion method:

Method JsonValue Subtype
asString JsonString
asInt JsonNumber
asLong JsonNumber
asDouble JsonNumber
asBoolean JsonBoolean
asMap JsonObject
asList JsonArray
For example, you might want to retrieve the Java `String` value associated with the thread dump's "time" member:
JsonValue threadDumpTime = threadDump.get("time"); // Access "time" first
    String time = threadDumpTime.asString();

Conversion methods can also be called on structural JSON values. For example, once you have navigated to the thread containers array, you can convert the value to a List of JsonValue, giving you the ability to process all elements:

threadContainers.asList().forEach(jv -> ...);

Or perhaps you might want to access the thread dump object as a Map to retrieve the number of members:

int count = threadDump.asMap().size();

You might want to navigate deeply into a JSON document, chaining access methods and converting to a Java type only at the end. For example, to retrieve the thread identifier value for the first thread in the root thread container:

long tid = threadDump.get("threadContainers").get(0)
        .get("threads").get(0).get("tid").asLong();

This design of conversion methods eliminates most instanceof checking and downcasting in cases where a specific JSON data type is expected in a document.

There is no conversion method for the JSON null value. JSON nulls can be handled with the tryValue() method or by testing for instanceof JsonNull; see JsonNull.

If a JsonValue is not an instance of the appropriate subtype for a conversion method, JsonValueException is thrown. 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 value into a number.

Numeric conversions can fail for reasons such as the numeric value not being representable in the requested Java numeric type. JsonValueException is also thrown in these cases. See Json numerics for a deeper discussion of number handling and conversions.

Handling evolution of JSON documents

JSON documents can change unpredictably, so your expectations about them might not always hold.

If you call access or conversion methods on the wrong type, they throw JsonValueException. This exception is unchecked, as it is not expected to be handled. This makes scripts and small programs easier to read and write.

Continuing the thread dump example from before, recall that the root JSON value contains a single member, "threadDump". The following code throws JsonValueException because "threadName" does not exist as a member:

JsonValue name = root.get("threadName");

While the following code throws JsonValueException because the "threadDump" member is a JSON object, not a JSON array:

List<JsonValue> threadDumpList = threadDump.asList();

The exception message describes the route leading from the root of the JSON document to the unexpected JSON value, as well as the position in the JSON document. This is helpful when a chain of access methods navigates deeply into the document. For example, if the earlier code snippet to access tid incorrectly converted it to a boolean instead of a long:

boolean tid = threadDump.get("threadContainers").get(0)
        .get("threads").get(0).get("tid").asBoolean();

The exception thrown by asBoolean() will appear similar to:

jdk.incubator.json.JsonValueException: JsonNumber is not a JsonBoolean.
  Path: "...". Location: line ..., position ...

Handling optional members and null values

If you know that a JSON object might or might not have a member with a given name, you can call the access method tryGet. This returns an Optional containing the value or an empty Optional if the member does not exist. (This contrasts with get, which confirms that the member exists and throws an exception if it does not.)

The tryGet() method will throw JsonValueException if it is not called on a JsonObject.

For example, consider the following thread object:

{
    "tid": 11,
    "time": "2026-04-16T23:13:02.918321Z",
    "name": "Finalizer",
    "state": "WAITING",
    "waitingOn": "java.lang.Object@c10f5b9",
    "stack": [
      ...

A thread object contains multiple optional members. One of them is the "waitingOn" member, which contains the JSON string representation of the object on which the thread is waiting. However, in cases where the thread is not waiting, the thread object may look like this:

{
    "tid": 10,
    "time": "2026-04-16T23:13:02.918177Z",
    "name": "Reference Handler",
    "state": "RUNNABLE",
    "stack": [
      ...

Thus, when processing thread objects from a thread dump, you may need to handle cases where the member you are interested in may be absent. You can handle this scenario using tryGet:

JsonValue thread = ...
    thread.tryGet("waitingOn")
        .ifPresent(result -> ...);

The operation within ifPresent is only performed if waitingOn is present.

If you know that a JSON value might or might not be a JSON null, you can process it with the method tryValue, which returns an Optional. If the JSON value is a JSON null, the Optional is empty; otherwise, the Optional contains a JsonValue that is not a JsonNull.

For example, a thread container object typically looks like this:

{
    "container": "java.util.concurrent.ThreadPoolExecutor@1936a586",
    "parent": "<root>",
    ...

Here, the "parent" member's value is a JSON string, the parent container's name. However, the container named "<root>" is the root of all containers and looks like this:

{
    "container": "<root>",
    "parent": null,
    ...

The root container has no parent, so the "parent" member's value is a JSON null. Thus, when processing container objects from a thread dump, you may need to handle the parent member being a JSON string or a JSON null. You can handle this scenario using tryValue:

JsonValue container = ...
    container.get("parent").tryValue()
        .ifPresent(result -> ...);

The operation within ifPresent is only performed if "parent" is not JSON null.

Handling structural variations

Most JSON documents have uniform structure, but it's possible for some JSON documents to have variable structure. This variation might occur across different documents produced by different software; or it might occur over time as the document generation software evolves; or it might even occur within the same document.

For example, thread dumps from JDK 27 and later contain thread identifiers as JSON numbers. However, thread dumps from JDK 26 and earlier contain thread identifiers as JSON strings. (This change was made by JDK-8381002, which was integrated in JDK 27, build 16.)

Code that successfully processes the tid as a JSON number:

long tid = thread.get("tid").asLong();

fails with a JsonValueException if it encounters a thread dump produced by a version of the JDK that emits tid as a JSON string.

You can write code that supports both variations of a thread identifier, noting that in either format, the value is specified to fit in a Java long. It's possible to use instanceof to check the type of the value, but in cases such as this, it may be more effective to use a type pattern within a switch statement:

long tid = switch (thread.get("tid")) {
        case JsonNumber jn -> jn.asLong();
        case JsonString js -> Long.parseLong(js.asString());
        default -> throw new JsonValueException("Unexpected type for \"tid\"");
    };

Generating JSON documents

It is often necessary to generate plain text from a JSON document. The toString() method returns a compact string representation where all members, elements, and values are emitted on the same line with no whitespace between them.

For example, this code:

JsonValue json = Json.parse("""
        {
            "service" : "web_server",
            "id" : 3
        }
        """);
    IO.println(json.toString());

prints:

{"service":"web_server","id":3}

Note that the behavior of toString() differs from asString(), which throws an exception if the JsonValue is not a JsonString.

The static method Json.toDisplayString() emits a "pretty-printed" version of a JSON document, where members and elements are separated by newlines, and nested structures are indented by the given amount. For example, use the following to print the above structure with two spaces of indentation:

IO.println(Json.toDisplayString(json, 2));

which prints:

{
    "service": "web_server",
    "id": 3
  }

The output of both toString() and Json.toDisplayString() are parsable by Json.parse() and will return a JSON structure that is equivalent to the original.

JSON numerics

RFC 8259 syntax allows decimal numbers with arbitrary precision and range, and the JDK JSON API allows JSON numbers to be processed losslessly. This is unnecessary for most applications, for which more common numeric types are sufficient. RFC 8259 states that good interoperability among JSON implementations can be achieved by using IEEE 754 64-bit binary floating point. This format corresponds to Java's double type.

The asDouble() method converts a numeric JSON value to a Java double. The JSON value must lie within the range that a double can represent. If the value is out of range, an exception is thrown; positive or negative infinity values are never returned. The double value NaN (not-a-number) cannot be represented in JSON and is also never returned. However, negative zero is supported.

If the JSON value has more precision than can be represented in a double, the value is rounded to the closest double value. For example:

double d1 = Json.parse("3.141592653589793238462643383279").asDouble();
    // d1 is 3.141592653589793, the nearest double value

    double d2 = Json.parse("1.8E309").asDouble();
    // throws JsonValueException, out of range

Integral numeric values are quite frequently used, so the asInt() method converts a numeric JSON value to a Java int value. The JSON value must be exactly representable as an int, otherwise an exception is thrown. Numbers that have a syntactic fractional part but that represent integral values are converted successfully. For example:

int i1 = Json.parse("123.0").asInt();       // succeeds
    int i2 = Json.parse("234.56E2").asInt();    // succeeds
    int i3 = Json.parse("345.6").asInt();       // fails
    int i4 = Json.parse("2147483648").asInt();  // fails

The conversion method asLong() is similar to asInt() except that it returns a Java long value and supports any JSON numeric value that can be represented exactly as a long.

If you need a narrower primitive type than int or double, you can use instanceof primitive matching to perform a safe conversion. (Primitive Types in Patterns is a preview feature.) For example, if you expect a JSON number to be representable with a short, you can do the following:

JsonValue json = Json.parse("""
        {
            "id": 12345,
            "price": 10.99
        }
        """);
    if (json.get("id").asInt() instanceof short s) {
        // use s
    } else {
        // report out-of-range error
    }

As mentioned previously, JSON decimal numbers can have arbitrary precision and range. The asDouble(), asInt(), and asLong() methods by definition process only a subset of JSON numeric values, as they either reject certain inputs or discard information via rounding. To preserve JSON numeric data without loss of information, you can convert essentially any JSON number to a Java BigDecimal by doing the following:

BigDecimal bd = new BigDecimal(json.toString());

Alternatives

The large set of external JSON libraries that already exist (including those mentioned previously) comprises an extremely broad feature set. The task is therefore to select a subset of these features suitable to include in the JDK. The selected features should provide the greatest value relative to their cost.

We've excluded the commonly provided feature of data binding. This is undeniably useful and convenient for many applications. However, it would add a significant API footprint and increase implementation and maintenance costs dramatically. Moreover, there are use cases that don't require data binding; we thus consider this feature not strictly necessary. That Jackson and Jakarta JSON factor data binding into separate modules is an implicit recognition that there are use cases that don't need data binding and that can exclude this dependency.

A streaming API is clearly essential for certain narrow, specialized use cases. However, it requires a fair amount of complexity in applications in order to perform relatively simple data extraction tasks. We've thus excluded this feature on that basis.

This left us with a DOM-like approach where JSON documents are parsed into a tree of JSON-specific objects from which data can be extracted easily. The API is extremely small, and it incurs correspondingly small implementation and maintenance costs. This satisfies the needs of a significant subset of JSON applications from the very simplest to the moderately complex.

It's possible for an application to start off using the JDK JSON API, but eventually for its needs to grow to include features such as data binding. Such an application will need to migrate to an external library that supports that feature. We don't view that as a flaw, and it isn't sufficient justification to include high-cost features such as data binding in the JDK.

The JDK could integrate an external library as a downstream fork. This would raise some difficult issues over licensing and governance. There would be continual tension over changes flowing in both directions, arising from potentially different criteria regarding specification quality, compatibility, release schedules, and so forth. It seems likely that these costs, plus the additional maintenance burden on the JDK, would outweigh the benefit of integrating an external library.

Since the JDK cannot have external dependencies, this approach would preclude the JDK itself from adding new features that produce and consume JSON.

Any application that adds an external dependency incurs a cost in doing so. There are probably applications that could benefit from using JSON but that do not, because their cost to add a dependency is too high. Such applications would benefit from having JSON support directly in the JDK.

This has been a longstanding issue with JSON. Early specifications were underdetermined with respect to the handling of duplicate member names. This led to a situation where implementations behaved inconsistently with each other or provided application-settable options to select the desired handling policy for duplicate names. Unfortunately, an object with duplicate names is fundamentally ambiguous. When the issue of duplicate names was discussed on the ES-Discuss mailing list in 2013, concern about prohibiting duplicate names was that doing so would invalidate existing documents. Thus, the "should be unique" wording (instead of "must") was retained, and it has been carried over to current specifications. In particular, RFC 8259 says

The names within an object SHOULD be unique. ... An object whose names are all unique is interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. When the names within an object are not unique, the behavior of software that receives such an object is unpredictable.

The unpredictability arises when the object is processed by a system consisting of multiple, independently-developed JSON implementations. This can lead to hard-to-diagnose errors, security vulnerabilities, decreased interoperability, and general lack of robustness. This phenomenon is discussed in RFC 9413, "Maintaining Robust Protocols".

For these reasons, we have chosen a strict approach where duplicate names are unconditionally treated as errors. The strict approach enables Java developers to have higher assurance over the correctness of documents being read. One hopes that the erroneous documents mentioned in the 2013 ES-Discuss conversation have been corrected in the intervening years, and that the software that produced those documents has been fixed.

There are several variants of JSON that support comments or trailing commas within arrays and objects. These options are intended to facilitate hand-editing of JSON documents; for example, see JSON5. Such options are not provided, given this project's focus on machine-to-machine communication.

Indeed, this implementation provides no parsing options at all. Adding options enlarges the testing matrix, opens the possibility for interoperability errors, and increases overall development and maintenance burden.

A common workaround is to use a custom preprocessor. For example, comments can be stripped out before parsing, which is arguably one of the main reasons the JSON syntax itself never introduced comments. The following simple snippet shows how to remove single-line comments (lines starting with #) prior to parsing:

String jsonc = Files.readString(Path.of("somefilewithcomments.json"));
    String json = jsonc.replaceAll("(?m)^\\s*#.*$", "");
    JsonValue jv = Json.parse(json);

Testing

JSON, which appears straightforward in syntax, has a history of varying behavior across different implementations. Having evolved through multiple specifications, differences in behavior have emerged over time in the handling of certain ambiguous and even malicious JSON inputs. As a last mover, the JDK JSON API benefits from being aware of the wide range of edge case inputs that previous implementations have encountered.

The JDK JSON API will be rigorously tested to ensure that only canonical forms of RFC 8259 JSON can be parsed and generated. This is important for ensuring that this library cannot be used to create inconsistencies when interacting with other JSON implementations within a larger system. To accomplish this, our testing approach will not only add comprehensive unit tests to the JDK and JCK, but also leverage the established conformance suite Parsing JSON is a Minefield, which contains numerous edge case inputs.

Risks and Assumptions

Appendix - Weather Forecast Example

The following example program issues a request to the U.S. National Weather Service REST API for a seven-day weather forecast for the lower Manhattan area of New York City. It receives a JSON document in the response body. The program then parses the document, navigates down a couple levels in the structure, and obtains an array of forecasts. The program continues by extracting the temperature from each forecast, averaging them, and printing the result.

import java.net.*;
    import java.net.http.*;
    import jdk.incubator.json.Json;
    import jdk.incubator.json.JsonValue;

    void main() throws Exception {
        var query = "https://api.weather.gov/gridpoints/OKX/33,42/forecast";
        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder(URI.create(query)).build();
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
        String body = response.body();
        JsonValue json = Json.parse(response.body());
        json.get("properties").get("periods").asList().stream()
            .mapToInt(j -> j.get("temperature").asInt())
            .average()
            .ifPresent(IO::println);
    }

Enabling the incubating API

The JSON API is an incubator module, which is disabled by default. It must be enabled using a command-line option --add-modules jdk.incubator.json that adds the incubator module to the set of modules available for resolution. To run this program, you must provide this option at run time and also at compile time if appropriate.

To run the average forecast program as a single-file source code program, run the following:

java --add-modules jdk.incubator.json Weather.java

The output will be something like this. (Of course, the average temperature will likely differ.)

WARNING: Using incubator modules: jdk.incubator.json
53.357142857142854

To compile the program with javac and run it with java, run the following:

javac --add-modules jdk.incubator.json Weather.java
java --add-modules jdk.incubator.json Weather

It's possible to use jshell to experiment interactively with the API. As before, it's necessary to supply a command line option to enable the incubator module:

jshell --add-modules jdk.incubator.json
jshell> import jdk.incubator.json.*
jshell> Json.parse("""
   ...> { "name": "Today", "temperature": 54 }
   ...> """)
$2 ==> {"name":"Today","temperature":54}
jshell> $2.get("temperature").asInt()
$3 ==> 54