The first think that annoyed me was the use of
Json.createObjectBuilder() and Json.createArrayBuilder() when trying to construct a JSONObject.So lets create a nice helper class with some very short method names for both:
public static JsonObjectBuilder ob() {
return Json.createObjectBuilder();
}
public static JsonArrayBuilder ab() {
return Json.createArrayBuilder();
}
This makes the creation of object just that little bit less wordy:
JsonObject object = ob()
.add("hello", "world")
.add("fark", ".com")
.add("fish", ob()
.add("child", "child"))
.add("array", ab()
.add("one")
.add("two")
.add(ob()
.add("boolean",true)))
.build();
The second problem is accessing element in a JsonObject can be wordy; but it is relatively easy to knock up a method that would allow some simply XPath like accessor:
public static <T extends JsonValue> T get(JsonStructure structure,
String path, Class<T> type) {
String segments[] = path.split("/");
JsonValue currentValue = structure;
for (String segment : segments) {
if (segment.length() == 0) {
continue;
}
if (currentValue instanceof JsonObject) {
JsonObject currentObject = (JsonObject) currentValue;
currentValue = currentObject.get(segment);
} else if (currentValue instanceof JsonArray) {
if (segment.startsWith("[") && segment.endsWith("]")) {
int index = Integer.parseInt(segment.substring(1, segment.length() - 1));
currentValue = ((JsonArray) currentValue).get(index);
} else {
throw new IllegalArgumentException("Array type requires key of the form [n]");
}
} else {
throw new IllegalStateException("Value types are not decomposible"
+ currentValue.getValueType());
}
}
return type.cast(currentValue);
}
// Example to get hold of a string value
System.out.println(get(object, "/fish/child", JsonString.class));
Of course even this is a little bit untidy as you have to care about the internal JsonValue types which is a pain; and deal with possibly null pointers without the aid of
Optional. It doesn't take much to wrap these up though.public static String getString(JsonStructure structure, String path) {
JsonString value = get(structure, path, JsonString.class);
return value != null ? value.getString() : null;
}
public static boolean is(JsonStructure structure, String path) {
JsonValue value = get(structure, path, JsonValue.class);
return value != null ? value == JsonValue.TRUE : false;
}
public static boolean isNull(JsonStructure structure, String path) {
JsonValue value = get(structure, path, JsonValue.class);
return value != null ? value == JsonValue.NULL : false;
}
public static BigInteger getBigDecimal(JsonStructure structure, String path) {
JsonNumber value = get(structure, path, JsonNumber.class);
return value != null ? value.bigIntegerValue() : null;
}
public static int getInt(JsonStructure structure, String path) {
JsonNumber value = get(structure, path, JsonNumber.class);
return value != null ? value.intValue() : null;
}
public static JsonArray getArray(JsonStructure structure, String path) {
return get(structure, path, JsonArray.class);
}
public static JsonObject getObject(JsonStructure structure, String path) {
return get(structure, path, JsonObject.class);
}
This means you can write more direct code such as:
if (is(object, "/array/[2]/boolean")) {
System.out.println(getString(object, "/fish/child"));
}
Sometimes it only take a few statically imported methods to make a API more useful / easier to read.

1 comment:
Nice article.
I've the same feeling about JSR-353. The basic stuff is there but you need custom helpers to make life easier.
My biggest paint point is that adding key/values to JsonObject throws NPE when value is null. This means a lot of of/else boilerplate before adding key/value pairs.
I've ended up using some utility class as well.
Post a Comment