Top
Best
New

Posted by theanonymousone 4 hours ago

JEP 540: Simple JSON API (Now in Incubator)(openjdk.org)
70 points | 47 comments
dfabulich 3 hours ago|
A stated goal of the API is to have "low ceremony"; this seems like a lot of ceremony.

    IO.println(JsonObject.of(Map.of("providers",
        JsonArray.of(List.of(JsonString.of("SUN"),
            JsonString.of("SunRsaSign"),
            JsonString.of("SunEC"))))));
There's gotta be a better way!

Surely there could be some way of creating a JsonArray of native Java Strings, Booleans, Doubles, and Integers without requiring clients to explicitly convert each value into a JsonValue. And why am I forced to convert a native List into a JsonArray just so I can make it the value of a JsonObject?

Why can't I write this?

   JsonObject.of(Map.of("providers", List.of("SUN", "SunRsaSign", SunEC"))));
owlstuffing 5 minutes ago||
You can inline native type-safe JSON directly in Java with the manifold project.

    /*[Dude.json/] {
      "Name": "Scott",
      "Age": 100,
      "Address": {
        "Street": "345 Syracuse Way",
        "City": "Atlantis"
      }
    }
    */

    Dude dude = Dude.fromSource();
            
    out.println(dude.getName());
    out.println(dude.getAge());
    out.println(dude.getAddress().getCity());

https://github.com/manifold-systems/manifold
nlitened 2 hours ago|||
There's a nice Java library called Clojure with a lightweight syntax if you need to work with data structures and concurrency in Java a lot:

    (println (json/generate-string {:providers ["SUN" "SunRsaSign" "SunEC"]}))
packetlost 55 minutes ago||
You really can't appreciate how awesome Clojure is until you're coming from Java, can you...
pron 2 minutes ago||
And vice versa. As someone who likes both languages, I appreciate how they both have their pros and cons. I was a Schemer before I learnt Java (when Java barely existed) and I adore Clojure, but if I were to write 10 MLOC mobile network routing and billing system, an air-traffic control system, or a credit-card transaction processing system etc. etc. that needs to evolve by a large team for 20 years, I would choose Java over Clojure any day.
pron 2 hours ago|||
I'm not involved with the design of this API, but it seems to me that the issue on the JSON generation side (where you're complaining about ceremony) is feature creep. Creating the API you want on top of the proposed one is trivial (even in user code), but then where do you stop? If you have that conversion, it seems reasonable to also support, say, sets and records; maybe even enums.

Indeed, Java JSON libraries typically offer all these conveniences and more, but this package is not intended to replace them - as the JEP clearly states ("It is not a goal to create an API that supplants established external JSON libraries"). It is intended to support only simple JSON tasks without requiring a library. For that goal, feature creep is particularly problematic: the more you offer (which reduces the need for the popular libraries in more situations) the greater the pressure to add even more.

Often, it's best to start with the minimum required, and then, as the API gets used in the field, see what the most valuable convenience methods to add on top.

dfabulich 1 hour ago||
POJOs and records require more configuration (e.g. Jackson's @JsonProperty, @JsonDeserialize). That could plausibly be out of scope.

But for constructing JSON out of strings, numbers, booleans, lists, and maps, there's really not that much scope to creep into.

Specifically, I think it would be perfectly cromulent to have JsonArray.of() be able to support any Iterable of native Strings, Integers, Doubles, or Booleans; that doesn't feel like feature creep to me at all. It would transparently support Sets. (Right now, the API only accepts Lists of JsonValues, which is what makes the API feel so ceremonious.)

HiJon89 15 minutes ago||
> Specifically, I think it would be perfectly cromulent to have JsonArray.of() be able to support any Iterable of native Strings, Integers, Doubles, or Booleans; that doesn't feel like feature creep to me at all.

What type would that method accept? Wouldn't it need to be Object? That seems worse than boilerplate

dtech 2 hours ago|||
Java lacks the expressiveness to make it much better than this. There's a reason almost all the "replacement Java" languages add something to support DSLs, e.g. type safe builders in Kotlin [1]

The kotlin stdlib-adjacent json lib does it like this

   buildJsonObject {
     putJsonArray("providers") {
        add("SUN")
        add("SunRsaSign")
        add("SunEC")
     }
   }
[1] https://kotlinlang.org/docs/type-safe-builders.html
pron 1 hour ago||
That's not the reason. The question was about JSON generation from existing Java types, not about a DSL. Java JSON libraries offer similar conveniences (in fact, you can be much more sophisticated, clear, and convenient than the DSL you've shown), but this particular library, as explicitly described in the JEP, is not intended to be a general-purpose JSON library, of which Java already has several good and popular ones.
dtech 1 hour ago||
That's not what the person I replied to asked at all, arbitrary types weren't mentioned.

They literally asked why they couldn't write JsonObject.of(Map.of(...))

pron 1 hour ago||
And there's nothing in Java preventing that. In fact, it is trivial to implement that exact API on top of the API proposed in the JEP.
mechanicum 2 hours ago|||
You almost can, with Jackson:

  jshell> new ObjectMapper().valueToTree(Map.of("providers", List.of("SUN", "SunRsaSign", "SunEC")));
  $4 ==> {"providers":["SUN","SunRsaSign","SunEC"]}
(or was that the joke?)
singron 1 hour ago|||
Currently, JsonObject.of has this signature:

    static JsonObject of(Map<String, ? extends JsonValue> map);
java.lang.String and other types don't extend JsonValue, and java lacks any trait-like way to add this functionality to existing types, so you would have to change the signature to this:

    static JsonObject of(Map<String, ? extends Object> map);
Now you can pass any Object in, but the typechecker can't ensure that it is convertible to json anymore. I.e. it will have to check at runtime that it's either JsonValue or another type that is has a known conversion for (Integer, Double, String, List, Map, etc.). The jackson ObjectMapper e.g. has a lot of configuration available to tell it how to do these conversions on arbitrary types, and I think they want to eliminate that kind of ceremony.

It does seem like any serious application is going to use another library, and this will be useful for very simple json usage or single-file hello-world type programs (e.g. to go along with Implicitly Defined Classes and the Flexible Launch Protocol).

hyperpape 43 minutes ago||
A Modest Proposal that will never be implemented: add an interface to String, Boolean, Integer, Long, Float and Double. Because all these types already implement "toString" (and I believe their toString representations are compatible with JSON), it can be a pure marker interface.
vlaaad 2 hours ago|||
Yes, what's the point of JsonObject at all? Why JsonString when there is String? Why JsonArray when there is List? JDK only needs a function to convert from JSON format to the normal data structures it already has and back.
pron 2 hours ago|||
Because JSON types don't cleanly map to Java types. For example, a JSON number could be an int, a long, a double, a BigInteger, or a BigDecimal. Which of them the Java code wants is not something you can deduce (most generally, you could represent all JSON numbers as BigDecimal, but that's not very convnient in Java code, so in most cases you'd want to convert that to a primitive type of your choosing anyway). A JSON array is heterogeneous, while Java code typically want to be homogeneous. Representing a JSON array as a `List<Object>` won't work because the conversion of the element types is ambiguous (e.g. numbers).
prpl 1 hour ago|||
Yeah you could go with just Number though, and then keep almost everything a BigInteger/BigDecimal under the covers (or dynamically choose the correct class)
pron 18 minutes ago||
You could, but it would be significantly worse. Number and JsonNumber are nearly equivalent in this context, except that JsonNumber has the major advantage of being in the same sealed hierarchy as the other JSON types, allowing for nice pattern-matching (and BigDecimal suffers from the same downside). So you gain nothing - you have to convert to the desired type anyway - and lose quite a bit. That should be obvious.
Hackbraten 1 hour ago|||
Good point for parsing. However, the example is about serialization. What mapping ambiguity do you see there?
dtech 2 hours ago|||
It's pretty normal, almost all libs work this way. This way you can parse the JSON string into the matching data structure or print it into a string, and when building you can ensure it's valid JSON.
marginalia_nu 1 hour ago|||
Yeah, I agree.

The clean way of doing this is to build a json marshaling mechanism for the type system as it already exists. This is doable in Java, and some json libraries (e.g. gson) are already capable of this.

I must admit I don't fully understand the motivation behind this JEP. Like when would I ever reach for this?

RedShift1 2 hours ago|||
minimal-json scratches this itch really well for me. Dead simple API. Unfortunately not maintained anymore, but I'm still using it, even for new projects, because it just works so well.
cute_boi 1 hour ago||
Even Rust syntax is so much clean and better than this...
whaley 3 hours ago||
Meanwhile, Jackson has been going strong for almost 20 years now https://github.com/FasterXML/jackson.

One of my favorite things about Jackson was being able to arbitrarily navigate through the document with a rich and fluent API (JsonNode) in jackson.databind, which this JEP at least conceptually borrows from with the JsonValue abstraction. Both of these are better than how some of the other implementations do it, where you are effectively working with glorified Map<String,Object>

rf15 2 hours ago|
But isn't JSON de facto conceptually a glorified Map<String,Object> ?
wewtyflakes 2 hours ago||
The root of a JSON document can also be an array.
svieira 2 hours ago||
Or the boolean `true`, or the empty string `""`, or `null`. JSON allows bare "value"s as the root element. https://www.json.org/json-en.html
esprehn 44 minutes ago||
Not supporting comments will be a mistake that haunts this API. They give an example of replacing properties files but those do have standardized comments! The proposed pre-processing step means all the comments are lost during round tripping, and the single line comments they suggest are not enough to even match JSONC. By the time those issues have userland workarounds you might as well use another library instead of the built-in one.

This seems to repeat the same mistakes of Go's built in JSON library where the ecosystem is full of workarounds and other libraries that are faster or have better features.

whartung 3 hours ago||
Well, this will be fun.

I already have one of these (I'm sure I'm not alone). It's about 500 lines.

I found I'm not a huge fan of the JAX-B-ish style serialization of Java objects for JSON. I don't want to really downplay them, they certainly have their uses, they're very popular, I just don't like fighting them. Hand writing JSON marshaling code has not been arduous for me (notably with my utility layer). (I also, philosophically, strive to avoid "magic" in my code as much as practical.)

Of course, I still need a parser, I'm using GSONs parser, which means I'm still dragging in the whole bean level serialization infrastructure. I just don't use it. And while I've done JSON parsers before, I felt it was something better to import than maintain myself. So, in that sense, it's a mixed bag.

But, I do enjoy using it.

This will be a worthwhile JDK capability. Ideally it can replace mine.

nikeee 1 hour ago||
I don't like this:

    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);
Why am I able to call `.get(string)` or `.get(int)` on a JsonValue? Shouldn't these be on the JsonObject and JsonArray instead?

> 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.

So if I get an exception, I can't tell whether the value was of the wrong type or the object didn't have the requested key? This looks like a footgun to me.

They just brought pattern matching to Java. Why not move those .get to JsonArray and JsonObject? That would solve this confusions. So we could just use something like `if (json instanceof JsonObject o) o.get("properties")`

gavinray 3 hours ago||
It used to be the case that if you wanted to build a web service on the JVM, you wanted 2 things:

1. An HTTP server library/framework

2. A JSON library

We got a decently-performing and unopionated HTTP server in JDK 18 with "HttpHandlers" and "SimpleFileServer" plus "jwebserver" CLI

It later received Virtual Thread support, which made performance + scalability very competitive.

With a JSON module, you finally won't NEED to rely on external deps to build a basic JVM web service without pain.

Now, we just need a proper CLI framework like picocli, or at least "argparse" from Python stdlib...

drdexebtjl 2 hours ago|
My understanding from reading this is the complete opposite. This library is explicitly not supporting the features that web servers need to be performant and handle production traffic, like streaming.

A web server using this could only start parsing when it receives the last byte, and could only start responding when it’s done serializing, all while holding non-lazy trees of JsonValue objects in memory.

wewtyflakes 2 hours ago||
I suspect the vast majority of services are not dealing with such massive JSON documents that doing serde on them becomes a material part of their latency breakdown.
Sankozi 3 hours ago||
Using JSON as a configuration format is a big mistake. JEP authors could learn a bit from package.json problems. Hope JSON will not be used in anything significant for JDK configuration.
Groxx 2 hours ago|
Yeah - json is fine as a readable mechanical exchange format, but without comments it's essentially unusable for anything humans need to touch :/

I'm somewhat boggled that json5 hasn't grown to be more of a thing.

IanGabes 3 hours ago||
Including this in the standard library speaks to how much a citizen JSON has become, where a language simply can't afford to not consider it.

I find it interesting to note that nowhere in this JEP is the word "serialization", which is what most people might associate with JSON libs. Or rather, they are studiously ignoring that feature and just improving the ergonomics of interacting with JSON.

mcfedr 3 hours ago||
its crazy its taken Java so long to realise this
ameliaquining 3 hours ago||
If I'm guessing correctly what you mean by "serialization", the JEP refers to it as "data binding" and includes a section on why it's not going to be part of this library.
MeteorMarc 3 hours ago||
Many languages have json marshalling and unmarshalling in their standard libs, e.g. C#, golang, python.
maleldil 1 hour ago||
Even a relatively barebones systems language like Zig has std.json. Odin has it too.
deepsun 2 hours ago||
You forgot Javascript :)
exabrial 3 hours ago|
Is this just including JSR-367 in the sdk? I've used that for years along with its many implementations. Great stuff!
More comments...