Hacker News
JEP 540: Simple JSON API (Now in Incubator)
dfabulich
|next
[-]
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
|root
|parent
|next
[-]
/*[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
|root
|parent
|next
|previous
[-]
(println (json/generate-string {:providers ["SUN" "SunRsaSign" "SunEC"]}))
packetlost
|root
|parent
[-]
pron
|root
|parent
[-]
pron
|root
|parent
|next
|previous
[-]
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
|root
|parent
[-]
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
|root
|parent
[-]
What type would that method accept? Wouldn't it need to be Object? That seems worse than boilerplate
dtech
|root
|parent
|next
|previous
[-]
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
|root
|parent
[-]
mechanicum
|root
|parent
|next
|previous
[-]
jshell> new ObjectMapper().valueToTree(Map.of("providers", List.of("SUN", "SunRsaSign", "SunEC")));
$4 ==> {"providers":["SUN","SunRsaSign","SunEC"]}
(or was that the joke?)
singron
|root
|parent
|next
|previous
[-]
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
|root
|parent
[-]
vlaaad
|root
|parent
|next
|previous
[-]
pron
|root
|parent
|next
[-]
prpl
|root
|parent
|next
[-]
pron
|root
|parent
[-]
marginalia_nu
|root
|parent
|next
|previous
[-]
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?
whaley
|next
|previous
[-]
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
|root
|parent
[-]
wewtyflakes
|root
|parent
[-]
svieira
|root
|parent
[-]
esprehn
|next
|previous
[-]
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
|next
|previous
[-]
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
|next
|previous
[-]
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
|next
|previous
[-]
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
|root
|parent
[-]
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.
Sankozi
|next
|previous
[-]
IanGabes
|next
|previous
[-]
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.
MeteorMarc
|next
|previous
[-]
exabrial
|next
|previous
[-]
q3k
|next
|previous
[-]
Does anyone know how this behaves when encountering a repeated key in an object? (RFC8259 states that keys SHOULD be unique, which makes that generally allowed and implementations all behave slightly differently).