From 3677c04f0a48634c7183db0e1f9330cb13f3be4e Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Tue, 27 Feb 2024 09:49:15 +0100 Subject: Various improvements to `Json` --- vespajlib/src/main/java/ai/vespa/json/Json.java | 31 +++++++++++++++++++++- .../src/test/java/ai/vespa/json/JsonTest.java | 7 ++++- 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'vespajlib') diff --git a/vespajlib/src/main/java/ai/vespa/json/Json.java b/vespajlib/src/main/java/ai/vespa/json/Json.java index b88c804c728..da7aae06e8d 100644 --- a/vespajlib/src/main/java/ai/vespa/json/Json.java +++ b/vespajlib/src/main/java/ai/vespa/json/Json.java @@ -8,6 +8,8 @@ import com.yahoo.slime.SlimeUtils; import com.yahoo.slime.Type; import java.math.BigDecimal; +import java.time.Instant; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -90,6 +92,20 @@ public class Json implements Iterable { return asBool(); } + public Optional asOptionalInstant() { return isMissing() ? Optional.empty() : Optional.of(asInstant()); } + public Instant asInstant() { + requireType(Type.STRING); + try { + return Instant.parse(asString()); + } catch (DateTimeParseException e) { + throw new InvalidJsonException("Expected JSON member '%s' to be a valid timestamp: %s".formatted(path, e.getMessage())); + } + } + public Instant asInstant(Instant defaultValue) { + if (isMissing()) return defaultValue; + return asInstant(); + } + public List toList() { var list = new ArrayList(length()); forEachEntry(json -> list.add(json)); @@ -184,7 +200,14 @@ public class Json implements Iterable { public static Builder.Array newArray() { return new Builder.Array(new Slime().setArray()); } public static Builder.Object newObject() { return new Builder.Object(new Slime().setObject()); } - public static Builder.Object existingObject(Cursor cursor) { return new Builder.Object(cursor); } + public static Builder.Object existingSlimeObjectCursor(Cursor cursor) { + if (cursor.type() != Type.OBJECT) throw new InvalidJsonException("Input is not an object"); + return new Builder.Object(cursor); + } + public static Builder.Array existingSlimeArrayCursor(Cursor cursor) { + if (cursor.type() != Type.ARRAY) throw new InvalidJsonException("Input is not an array"); + return new Builder.Array(cursor); + } private Builder(Cursor cursor) { this.cursor = cursor; } @@ -200,6 +223,8 @@ public class Json implements Iterable { public Builder.Array add(Json json) { SlimeUtils.addValue(json.inspector, cursor.addObject()); return this; } + public Builder.Array add(Json.Builder builder) { return add(builder.build()); } + /** Note: does not return {@code this}! */ public Builder.Array addArray() { return new Array(cursor.addArray()); } /** Note: does not return {@code this}! */ @@ -223,6 +248,9 @@ public class Json implements Iterable { public Builder.Object set(String field, Json json) { SlimeUtils.setObjectEntry(json.inspector, field, cursor); return this; } + public Builder.Object set(String field, Json.Builder json) { + SlimeUtils.setObjectEntry(json.build().inspector, field, cursor); return this; + } /** Note: does not return {@code this}! */ public Builder.Array setArray(String field) { return new Array(cursor.setArray(field)); } /** Note: does not return {@code this}! */ @@ -233,6 +261,7 @@ public class Json implements Iterable { public Builder.Object set(String field, double value) { cursor.setDouble(field, value); return this; } public Builder.Object set(String field, boolean value) { cursor.setBool(field, value); return this; } public Builder.Object set(String field, BigDecimal value) { cursor.setString(field, value.toPlainString()); return this; } + public Builder.Object set(String field, Instant timestamp) { cursor.setString(field, timestamp.toString()); return this; } } public Cursor slimeCursor() { return cursor; } diff --git a/vespajlib/src/test/java/ai/vespa/json/JsonTest.java b/vespajlib/src/test/java/ai/vespa/json/JsonTest.java index 293e99227a7..51b64637fd8 100644 --- a/vespajlib/src/test/java/ai/vespa/json/JsonTest.java +++ b/vespajlib/src/test/java/ai/vespa/json/JsonTest.java @@ -23,7 +23,8 @@ class JsonTest { "array": [1, 2, 3], "quux": { "corge": "grault" - } + }, + "timestamp": "2021-06-01T12:00:00Z" } """; var json = Json.of(text); @@ -37,6 +38,7 @@ class JsonTest { assertEquals(8.25D, json.f("floaty").asDouble()); assertEquals(8L, json.f("floaty").asLong()); assertTrue(json.f("bool").asBool()); + assertEquals("2021-06-01T12:00:00Z", json.f("timestamp").asInstant().toString()); // Array member assertEquals(3, json.f("array").length()); @@ -78,6 +80,9 @@ class JsonTest { exception = assertThrows(InvalidJsonException.class, () -> json.f("string").asLong()); assertEquals("Expected JSON member 'string' to be a 'integer' or 'float' but got 'string'", exception.getMessage()); + + exception = assertThrows(InvalidJsonException.class, () -> json.f("string").asInstant()); + assertEquals("Expected JSON member 'string' to be a valid timestamp: Text 'bar' could not be parsed at index 0", exception.getMessage()); } @Test -- cgit v1.2.3