(println (json/generate-string {:providers ["SUN" "SunRsaSign" "SunEC"]}))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.htmlThey literally asked why they couldn't write JsonObject.of(Map.of(...))
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.
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.)
First, we've been in this game far too long to know that this isn't the case. Second, this is only incubation. It may well be that the team behind this feature intend to add more convenience methods but wish to do it later once the core is more battle-tested. It's always best to focus on the core first and add ornamentation once you know the core is right.
What type would that method accept? Wouldn't it need to be Object? That seems worse than boilerplate
val json = JsonObject(
mapOf(
"providers" to JsonArray(
listOf(
JsonPrimitive("SUN"),
)
)
)
)
println(json)
Of course nobody generally does it this way, usually you take a List/Map and directly serialize that with a helper val data = mapOf("providers" to listOf("SUN", "SunRsaSign", "SunEC"))
val kotlinxJSON = Json.encodeToJsonElement(data)
val jacksonJSON = ObjectMapper().writeValueAsString(data) /*[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 jshell> new ObjectMapper().valueToTree(Map.of("providers", List.of("SUN", "SunRsaSign", "SunEC")));
$4 ==> {"providers":["SUN","SunRsaSign","SunEC"]}
(or was that the joke?) 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).
This would help a lot:
public interface JsonArray extends JsonValue {
static JsonArray of(JsonValue... elements) { ... }
static JsonArray of(String... elements) { ... }
static JsonArray of(Double... elements) { ... }
static JsonArray of(Integer... elements) { ... }
static JsonArray of(Boolean... elements) { ... }
}
Then, you could at least write: IO.println(JsonObject.of(Map.of("providers",
JsonArray.of("SUN", "SunRsaSign", "SunEC"))));
And for JsonObject, a little fluent builder API would probably knock out a lot of ceremony, too. JsonObject json = JsonObject.builder()
.put("name", "John")
.put("age", 30)
.put("active", true)
.put("providers", JsonArray.of("SUN", "SunEC"))
.build();
The alternative today looks quite ceremonious: JsonObject json= JsonObject.of(Map.of(
"name", JsonString("John"),
"age", JsonNumber(30),
"active", JsonBoolean(true),
"providers", JsonArray.of(List.of(
JsonString("SUN"),
JsonString("SunRsaSign"),
JsonString("SunEC")
))
));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?
As I understand that section (and I wasn't involved in writing this JEP), good and popular marshalling libraries for JSON already exist, and the JEP clearly states that it is not the goal to replace them or perform their role. The JEP says that this package may be what you'd reach for when a program only wants to do some very simple, small tasks with JSON data and the requirements and code size don't merit pulling in a fully-featured JSON library (e.g. when you're writing a one-file script, or exploring in JShell).