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