<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p><br>
Great work.</p>
<p><br>
</p>
<p>I feel the elephant in the room needs to addressed: JSON
comments. I haven't tested the proposed lib but I cannot see it
mentioned so I'm assuming that comments are not supported.<br>
</p>
<p>For better or worse, the use of jsonc (JSON with comments) is
everywhere in some ecosystems. Unsurprisingly this happens often
when JSON is used as a config file format. Looking at you,
Microsoft.</p>
<p>It would be nice if the JDK's build-in JSON parser at least
recognized this.</p>
<p>I'm well aware that comments are frowned upon in JSON and not
part of neither the spec at <a class="moz-txt-link-abbreviated" href="http://www.json.org">www.json.org</a> nor the RFC-8259.</p>
<p>Yet, I advocate the JDK JSON library should optionally allow
comments to be ignored when PARSING. This should be an opt-in
feature that would technically treat comments as whitespace during
the parsing process.<br>
</p>
<p>This would also be in line with what many other parsers do. For
example, Jackson has "ALLOW_COMMENTS" feature [1]. Also, by
comparison, the build-in parser in the .NET world, known as
System.Text.Json, also supports this [2]. <br>
</p>
<p><br>
</p>
<p><br>
</p>
<p>The "discoverer" of JSON, Douglas Crowford, had this to say [3]
on the topic:</p>
<p><br>
</p>
<p>[QUOTE]<br>
</p>
<p>I removed comments from <abbr title="JavaScript Object Notation">JSON</abbr>
because I saw people were using them to hold parsing directives, a
practice which would have destroyed interoperability. I know that
the lack of comments makes some people sad, but it shouldn't. </p>
<p>Suppose you are using <abbr title="JavaScript Object Notation">JSON</abbr>
to keep configuration files, which you would like to annotate. Go
ahead and insert all the comments you like. Then pipe it through
JSMin before handing it to your <abbr
title="JavaScript Object Notation">JSON</abbr> parser.</p>
<p> [/QUOTE]</p>
<p></p>
<p><br>
</p>
<p>By not having the ability to ignore comments when parsing we
would effectively force users to use another parser first or a
minifier. I doubt beginners would appreciate that. <br>
</p>
<p><br>
</p>
<p>BTW: The test suite already has tests for comments.</p>
<p><br>
</p>
<p>/Lars<br>
</p>
<p><br>
</p>
<p>[1]:
<a class="moz-txt-link-freetext" href="https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.19.0/com/fasterxml/jackson/core/JsonParser.Feature.html#ALLOW_COMMENTS">https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.19.0/com/fasterxml/jackson/core/JsonParser.Feature.html#ALLOW_COMMENTS</a></p>
<p></p>
<p>[2]:
<a class="moz-txt-link-freetext" href="https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonreaderoptions?view=net-9.0#properties">https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonreaderoptions?view=net-9.0#properties</a></p>
<p>[3]:
<a class="moz-txt-link-freetext" href="https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr">https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr</a></p>
<p><br>
</p>
<p><br>
</p>
<p><br>
</p>
<div class="moz-cite-prefix">On 16/05/2025 01.44, Ethan McCue wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CA+NR86gt3GNjDPS2m-2wha5AwgumMvBRxKw+bwgTCNQ1xCiDfg@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div dir="ltr">I present for your consideration the library I made
when spiraling about this problem space a few years ago<br>
<br>
<a href="https://github.com/bowbahdoe/json" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">https://github.com/bowbahdoe/json</a><br>
<br>
<a
href="https://javadoc.io/doc/dev.mccue/json/latest/dev.mccue.json/dev/mccue/json/package-summary.html"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://javadoc.io/doc/dev.mccue/json/latest/dev.mccue.json/dev/mccue/json/package-summary.html</a><br>
<br>
Notably missing during the design process here were patterns,
hence the JsonDecoder design. I haven't been able to evaluate
how patterns affect that on account of them not being out.<br>
<br>
I will more thoroughly peruse the draft of java.util.json at a
later date, but my initial observations/comments:<br>
<br>
* I am not sure having JsonValue be distinct from Json has
value.<br>
* toUntyped feels a little strange to me - the only type
information presumably lost is the sealed-ness of the hierarchy.
The interplay between that and toNumber is also a little
unnerving.<br>
* One notion that I found helpful was that a class could be
"json encodable," meaning there is a method to call to obtain a
canonical json representation.<br>
<br>
<font face="monospace">record Person(String name) implements
JsonEncodable {</font><br>
<font face="monospace"> </font><a class="gmail_plusreply"
id="m_-2004856809941181964plusReplyChip-0"
style="font-family:monospace" moz-do-not-send="true">@Override<br>
</a><font face="monospace"> public Json toJson() {</font><br>
<font face="monospace"> return Json.objectBuilder()</font><br>
<font face="monospace"> .put("namen", name)</font><br>
<font face="monospace"> .build();</font><br>
<font face="monospace"> }</font><br>
<font face="monospace">}</font><br>
<br>
<font face="arial, sans-serif">Which helper methods like </font><font
face="monospace">Json#of(List<? extends JsonEncodable>)</font><font
face="arial, sans-serif"> could make use of. </font><font
face="monospace">Json</font><font face="arial, sans-serif"> itself
(</font><font face="monospace">JsonValue</font><font
face="arial, sans-serif"> in your prototype) could then have a
vacuous implementation.<br>
<br>
* Terminology wise - I went with reading/writing for the
actual parsing/generation of json and encoding/decoding for
the mapping of those representations to/from specific classes.
The merits are not top of mind, just noting the difference.
read/write vs parse/toString+toDisplayString<br>
* One thing I did was make the helper methods in </font><font
face="monospace">Json</font><font face="arial, sans-serif"> null
tolerant and the ones in the specific subtypes like </font><font
face="monospace">JsonString</font><font
face="arial, sans-serif"> not. This was because from what I
saw of usages of javax.json/jakarta.json that nullability was
a footgun and correcting for it required changes to code
structure (breaking up builder chains with </font><font
face="monospace">if (x != null)</font><font
face="arial, sans-serif"> checks)<br>
</font>* The functionality you want from <font face="monospace">JsonNumber
</font><font face="arial, sans-serif">could be achieved by
making it just extend </font><font face="monospace">Number (</font><a
href="https://github.com/bowbahdoe/json/blob/main/src/main/java/dev/mccue/json/JsonNumber.java"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://github.com/bowbahdoe/json/blob/main/src/main/java/dev/mccue/json/JsonNumber.java</a>)
instead of a bespoke <font face="monospace">toNumber</font><font
face="arial, sans-serif">. You need the extra methods to go to
big decimal and co, but it's just an extension to the behavior
of Number at that point.<br>
</font>* JsonObject and JsonArray could implement Map<String,
Json> and List<Json> respectively. This lowers the need
for toUntyped() - since presumably one of the use cases for that
is turning the json tree into something that more generic
map/list traversal code can handle. It also complicates any lazy
loading somewhat.<br>
* Assuming patterns can be placed on interfaces, you might want
to consider something similar to <font face="monospace">JsonDecoder</font><font
face="arial, sans-serif">, but with a pattern instead of a
method that throws an exception.<br>
</font><br>
<span style="font-family:monospace">// Where here fromJson would
box up the logic for testing and extracting from each element
in the array.</span><br>
<font face="monospace">List<Person> people = array(json,
Person::fromJson);<br>
</font><br>
* I don't think there is sufficient cause for anything to be
non-sealed at this point.<br>
* JsonBoolean and JsonNull do not have reasonable alternative
implementations - as far as I can imagine, maybe i'm wrong - so
maybe those can just be final classes?<br>
* If you seal up the whole hierarchy then its pretty trivial to
make it serializable (<a
href="https://github.com/bowbahdoe/json/blob/main/src/main/java/dev/mccue/json/serialization/JsonSerializationProxy.java"
moz-do-not-send="true" class="moz-txt-link-freetext">https://github.com/bowbahdoe/json/blob/main/src/main/java/dev/mccue/json/serialization/JsonSerializationProxy.java</a>)<br>
<br>
<br>
<br>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Thu, May 15, 2025 at
11:29 PM Remi Forax <<a href="mailto:forax@univ-mlv.fr"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">forax@univ-mlv.fr</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote"
style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi
Paul,<br>
yes, not having a simple JSON API in Java is an issue for
beginners.<br>
<br>
It's not clear to me why JsonArray (for example) has to be an
interface instead of a record ?<br>
<br>
I understand why Json.parse() only works on String and char[]
but the API make it too easy to have many performance issues.<br>
I think you need versions using a Reader and a Path.<br>
Bonus point, if there is a method walk() that also returns a
JsonValue but the List/Map inside JsonArray/JsonObject are
populated lazily.<br>
<br>
Minor point: Json.toDisplayString() should takes a second
parameters indicating the number of spaces used for the
indentation (like JSON.stringify in JS).<br>
<br>
regards,<br>
Rémi<br>
<br>
----- Original Message -----<br>
> From: "Paul Sandoz" <<a
href="mailto:paul.sandoz@oracle.com" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">paul.sandoz@oracle.com</a>><br>
> To: "core-libs-dev" <<a
href="mailto:core-libs-dev@openjdk.org" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">core-libs-dev@openjdk.org</a>><br>
> Sent: Thursday, May 15, 2025 10:30:42 PM<br>
> Subject: Towards a JSON API for the JDK<br>
<br>
> Hi,<br>
> <br>
> We would like to share with you our thoughts and plans
towards a JSON API for<br>
> the JDK.<br>
> Please see the document below.<br>
> <br>
> -<br>
> <br>
> We have had the pleasure of using a clone of this API in
some experiments we are<br>
> conducting with<br>
> ONNX and code reflection [1]. Using the API we were able
to quickly write code<br>
> to ingest and convert<br>
> a JSON document representing ONNX operation schema into
instances of records<br>
> modeling the schema<br>
> (see here [2]).<br>
> <br>
> The overall out-of-box experience with such a minimal
"batteries included” API<br>
> has so far been positive.<br>
> <br>
> Thanks,<br>
> Paul.<br>
> <br>
> [1] <a href="https://openjdk.org/projects/babylon/"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://openjdk.org/projects/babylon/</a><br>
> [2]<br>
> <a
href="https://github.com/openjdk/babylon/blob/code-reflection/cr-examples/onnx/opgen/src/main/java/oracle/code/onnx/opgen/OpSchemaParser.java#L87"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://github.com/openjdk/babylon/blob/code-reflection/cr-examples/onnx/opgen/src/main/java/oracle/code/onnx/opgen/OpSchemaParser.java#L87</a><br>
> <br>
> # Towards a JSON API for the JDK<br>
> <br>
> One of the most common requests for the JDK is an API for
parsing and generating<br>
> JSON. While JSON originated as a text-based serialization
format for JSON<br>
> objects ("JSON" stands for "JavaScript Object Notation"),
because of its simple<br>
> and flexible syntax, it eventually found use outside the
JavaScript ecosystem as<br>
> a general data interchange format, such as framework
configuration files and web<br>
> service requests/response formats.<br>
> <br>
> While the JDK cannot, and should not, provide libraries
for every conceivable<br>
> file format or protocol, the JDK philosophy is one of
"batteries included",<br>
> which is to say we should be able to write basic programs
that use common<br>
> protocols such as HTTP, without having to appeal to third
party libraries.<br>
> The Java ecosystem already has plenty of JSON libraries,
so inclusion in<br>
> the JDK is largely meant to be a convenience, rather than
needing to be the "one<br>
> true" JSON library to meet the needs of all users. Users
with specific needs<br>
> are always free to select one of the existing third-party
libraries.<br>
> <br>
> ## Goals and requirements<br>
> <br>
> Our primary goal is that the library be simple to use for
parsing, traversing,<br>
> and generating conformant JSON documents. Advanced
features, such as data<br>
> binding or path-based traversal should be possible to
implement as layered<br>
> features, but for simplicity are not included in the core
API. We adopt a goal<br>
> that the performance should be "good enough", but where
performance<br>
> considerations conflict with simplicity and usability, we
will choose in favor<br>
> of the latter.<br>
> <br>
> ## API design approach<br>
> <br>
> The description of JSON at `https:://<a
href="http://json.org" rel="noreferrer" target="_blank"
moz-do-not-send="true">json.org</a>` describes a JSON
document using<br>
> the familiar "railroad diagram":<br>
> <br>
> <br>
> This diagram describes an algebraic data type (a sum of
products), which we<br>
> model directly with a set of Java interfaces:<br>
> <br>
> ```<br>
> interface JsonValue { }<br>
> interface JsonArray extends JsonValue {
List<JsonValue> values(); }<br>
> interface JsonObject extends JsonValue { Map<String,
JsonValue> members(); }<br>
> interface JsonNumber extends JsonValue { Number
toNumber(); }<br>
> interface JsonString extends JsonValue { String value();
}<br>
> interface JsonBoolean extends JsonValue { boolean
value(); }<br>
> interface JsonNull extends JsonValue { }<br>
> ```<br>
> <br>
> These interfaces have (hidden) companion implementation
classes that admit<br>
> greater flexibility of implementation than modeling them
directly with records<br>
> would permit.<br>
> Further, these interfaces are unsealed. We compromise on
the sealed sum of<br>
> products to enable<br>
> alternative implementations, for example to support
alternative formats that<br>
> encode the same information in a JSON document but in a
more efficient form than<br>
> text.<br>
> <br>
> The API has static methods for parsing strings into a
`JsonValue`, conversion to<br>
> and from purely untyped representations (lists and maps),
and factory methods<br>
> for building JSON documents. We apply composition
consistently, e.g, a<br>
> JsonString has a string, a JsonObject has a map of string
to JsonValue, as<br>
> opposed to extension for structural JSON values.<br>
> <br>
> It turns out that this simple API is almost all we need
for traversal. It gives<br>
> us an immutable representation of a document, and we can
use pattern matching to<br>
> answer the myriad questions that will come up (Does this
object have key X? Does<br>
> it map to a number? Is that number representable as an
integer?) when going<br>
> from an untyped format like JSON to a more strongly typed
domain model.<br>
> Given a simple document like:<br>
> <br>
> ```<br>
> {<br>
> "name": "John”,<br>
> "age": 30<br>
> }<br>
> ```<br>
> <br>
> we can parse and traverse the document as follows:<br>
> <br>
> ```<br>
> JsonValue doc = Json.parse(inputString);<br>
> if (doc instanceof JsonObject o<br>
> && o.members().get("name") instanceof
JsonString s<br>
> && s.value() instanceof String name<br>
> && o.members().get("age") instanceof
JsonNumber n<br>
> && n.toNumber() instanceof Long l && l
instanceof int age) {<br>
> // use "name" and "age"<br>
> }<br>
> ```<br>
> <br>
> Later, when the language acquires the ability to expose
deconstruction patterns<br>
> for arbitrary interfaces (similar to today's record
patterns, see<br>
> <a
href="https://openjdk.org/projects/amber/design-notes/patterns/towards-member-patterns"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://openjdk.org/projects/amber/design-notes/patterns/towards-member-patterns</a>),<br>
> this will be simplifiable to:<br>
> <br>
> ```<br>
> JsonValue doc = Json.parse(inputString);<br>
> if (doc instanceof JsonObject(var members)<br>
> && members.get("name") instanceof
JsonString(String name)<br>
> && members.get("age") instanceof
JsonNumber(int age)) {<br>
> // use "name" and "age"<br>
> }<br>
> ```<br>
> <br>
> So, overtime, as more pattern matching features are
introduced we anticipate<br>
> improved use of the API. This is a primary reason why the
API is so minimal.<br>
> Convenience methods we add today, such as a method that
accesses a JSON<br>
> object component as say a JSON string or throws an
exception, will become<br>
> redundant in the future.<br>
> <br>
> ## JSON numbers<br>
> <br>
> The specification of JSON number makes no explicit
distinction between integral<br>
> and decimal numbers, nor specifies limits on the size of
those numbers.<br>
> This is a common source of interoperability issues when
consuming JSON<br>
> documents. Generally users cannot always but often do
assume JSON numbers are<br>
> parsable, without loss of precision, to IEEE
double-precision floating point<br>
> numbers or 32-bit signed integers.<br>
> <br>
> In this respect the API provides three means to operate
on the JSON number,<br>
> giving the user full control:<br>
> <br>
> 1. Underlying string representation can be obtained, if
preserving syntactic<br>
> details such as leading or trailing zeros is important.<br>
> 2. The string representation can be parsed to an instance
of `BigDecimal`, using<br>
> `toBigDecimal` if preserving decimal numbers is
important.<br>
> 3. The string representation can be parsed into an
instance of `Long`, `Double`,<br>
> `BigInteger`, or `BigDecimal`, using `toNumber`. The
result of this method<br>
> depends on how the representation can be parsed,
possibly losing precision,<br>
> choosing a suitably convenient numeric type that can
then be pattern<br>
> matched on.<br>
> <br>
> Primitive pattern matching will help as will further
pattern matching features<br>
> enabling the user to partially match.<br>
> <br>
> ## Prototype implementation<br>
> <br>
> The prototype implementation is currently located into
the JDK sandbox<br>
> repository<br>
> under the `json` branch, see<br>
> here<br>
> <a
href="https://github.com/openjdk/jdk-sandbox/tree/json/src/java.base/share/classes/java/util/json"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://github.com/openjdk/jdk-sandbox/tree/json/src/java.base/share/classes/java/util/json</a><br>
> The prototype API javadoc generated from the repository
is also available at<br>
> <a
href="https://cr.openjdk.org/~naoto/json/javadoc/api/java.base/java/util/json/package-summary.html"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://cr.openjdk.org/~naoto/json/javadoc/api/java.base/java/util/json/package-summary.html</a><br>
> <br>
> ### Testing and conformance<br>
> <br>
> The prototype implementation passes all conformance test
cases but two,<br>
> available<br>
> on <a href="https://github.com/nst/JSONTestSuite"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://github.com/nst/JSONTestSuite</a>.
The two exceptions are the ones which<br>
> the<br>
> prototype specifically prohibits, i.e, duplicated names
in JSON objects<br>
> (<a
href="https://cr.openjdk.org/~naoto/json/conformance/results/parsing.html#35"
rel="noreferrer" target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://cr.openjdk.org/~naoto/json/conformance/results/parsing.html#35</a>).<br>
> <br>
> ### Performance<br>
> <br>
> Our main focus so far has been on the API design and a
functional<br>
> implementation.<br>
> Hence, there has been less focus on performance even
though we know there are a<br>
> number of performance enhancements we can make
eventually.<br>
> We are reasonably happy with the current performance. The<br>
> implementation performs well when compared to other JSON
implementations<br>
> parsing from string instances and traversing documents.<br>
> <br>
> An example of where we may choose simplicity and
usability over performance<br>
> is the rejection of JSON documents containing objects
that in turn contain<br>
> members<br>
> with duplicate names. That may increase the cost of
parsing, but simplifies the<br>
> user<br>
> experience for the majority of cases since if we
reasonably assume JsonObjects<br>
> are<br>
> map-like, what should the user do with such members, pick
one the last one?<br>
> merge<br>
> the values? or reject?<br>
> <br>
> ## A JSON JEP?<br>
> <br>
> We plan to draft JEP when we are ready. Attentive readers
will observe that<br>
> a JEP already exists, JEP 198: Light-Weight JSON API<br>
> (<a href="https://openjdk.org/jeps/198" rel="noreferrer"
target="_blank" moz-do-not-send="true"
class="moz-txt-link-freetext">https://openjdk.org/jeps/198</a>).
We will<br>
> either update this JEP, or withdraw it and draft a new
one.<br>
</blockquote>
</div>
</blockquote>
</body>
</html>