diff --git a/json-path/src/main/java/com/jayway/jsonpath/ParseContext.java b/json-path/src/main/java/com/jayway/jsonpath/ParseContext.java index 7aad7292..eb90a057 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/ParseContext.java +++ b/json-path/src/main/java/com/jayway/jsonpath/ParseContext.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; + /** * Parses JSON as specified by the used {@link com.jayway.jsonpath.spi.json.JsonProvider}. */ @@ -34,6 +35,8 @@ public interface ParseContext { DocumentContext parse(File json) throws IOException; + DocumentContext parseUtf8(byte[] json); + @Deprecated DocumentContext parse(URL json) throws IOException; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java index af330977..fcfa1c68 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java @@ -38,6 +38,13 @@ public class ParseContextImpl implements ParseContext { return new JsonContext(obj, configuration); } + @Override + public DocumentContext parseUtf8(byte[] json) { + notEmpty(json, "json bytes can not be null or empty"); + Object obj = configuration.jsonProvider().parse(json); + return new JsonContext(obj, configuration); + } + @Override public DocumentContext parse(InputStream json) { return parse(json, "UTF-8"); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java index 3e54c49f..e51c874f 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java @@ -403,6 +403,26 @@ public final class Utils { return chars; } + /** + *

Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + *

+ *

Validate.notEmpty(myString, "The string must not be empty");
+ * + * @param bytes the bytes to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is empty + */ + public static byte[] notEmpty(byte[] bytes, String message) { + if (bytes == null || bytes.length == 0) { + throw new IllegalArgumentException(message); + } + return bytes; + } + /** *

Validate that the specified argument character sequence is * neither {@code null} nor a length of zero (no characters); diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java index 2b8f6e21..e34277f9 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java @@ -8,17 +8,18 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.jayway.jsonpath.InvalidJsonException; import com.jayway.jsonpath.JsonPathException; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; + public class JacksonJsonNodeJsonProvider extends AbstractJsonProvider { private static final ObjectMapper defaultObjectMapper = new ObjectMapper(); @@ -54,6 +55,16 @@ public class JacksonJsonNodeJsonProvider extends AbstractJsonProvider { } } + @Override + public Object parse(byte[] json) + throws InvalidJsonException { + try { + return objectMapper.readTree(json); + } catch (IOException e) { + throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8)); + } + } + @Override public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { try { diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java index f23fe1a1..32522f55 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java @@ -18,15 +18,16 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.jayway.jsonpath.InvalidJsonException; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; + public class JacksonJsonProvider extends AbstractJsonProvider { private static final ObjectMapper defaultObjectMapper = new ObjectMapper(); @@ -73,6 +74,16 @@ public class JacksonJsonProvider extends AbstractJsonProvider { } } + @Override + public Object parse(byte[] json) + throws InvalidJsonException { + try { + return objectReader.readValue(json); + } catch (IOException e) { + throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8)); + } + } + @Override public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { try { diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JakartaJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JakartaJsonProvider.java index 2f11ce0b..366de223 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JakartaJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JakartaJsonProvider.java @@ -1,5 +1,20 @@ package com.jayway.jsonpath.spi.json; +import com.jayway.jsonpath.InvalidJsonException; +import com.jayway.jsonpath.JsonPathException; +import jakarta.json.JsonArray; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonReader; +import jakarta.json.JsonString; +import jakarta.json.JsonStructure; +import jakarta.json.JsonValue; +import jakarta.json.spi.JsonProvider; +import jakarta.json.stream.JsonParsingException; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; @@ -7,6 +22,7 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; @@ -19,22 +35,6 @@ import java.util.ListIterator; import java.util.Map; import java.util.Set; -import com.jayway.jsonpath.InvalidJsonException; -import com.jayway.jsonpath.JsonPathException; - -import jakarta.json.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonException; -import jakarta.json.JsonNumber; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.json.JsonReader; -import jakarta.json.JsonString; -import jakarta.json.JsonStructure; -import jakarta.json.JsonValue; -import jakarta.json.spi.JsonProvider; -import jakarta.json.stream.JsonParsingException; public class JakartaJsonProvider extends AbstractJsonProvider { @@ -69,34 +69,34 @@ public class JakartaJsonProvider extends AbstractJsonProvider { @Override public Object parse(String json) throws InvalidJsonException { - Reader jsonInput = new StringReader(json); - try (JsonReader jsonReader = defaultJsonProvider.createReader(jsonInput)) { - JsonStructure jsonStruct = jsonReader.read(); - return mutableJson ? proxyAll(jsonStruct) : jsonStruct; - } catch (JsonParsingException e) { - throw new InvalidJsonException(e); - } - // not catching a JsonException as it never happens here + return parse(new StringReader(json)); + } + + @Override + public Object parse(byte[] json) + throws InvalidJsonException { + return parse(new InputStreamReader(new ByteArrayInputStream(json), StandardCharsets.UTF_8)); } @Override public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException { - Reader jsonInput; try { - jsonInput = new InputStreamReader(jsonStream, charset); + return parse(new InputStreamReader(jsonStream, charset)); } catch (UnsupportedEncodingException e) { throw new JsonPathException(e); } - try (JsonReader jsonReader = defaultJsonProvider.createReader(jsonInput)) { - JsonStructure jsonStruct = jsonReader.read(); - return mutableJson ? proxyAll(jsonStruct) : jsonStruct; - } catch (JsonParsingException e) { - throw new InvalidJsonException(e); - } catch (JsonException e) { - throw new JsonPathException(e); - } } + private Object parse(Reader jsonInput) { + try (JsonReader jsonReader = defaultJsonProvider.createReader(jsonInput)) { + JsonStructure jsonStruct = jsonReader.read(); + return mutableJson ? proxyAll(jsonStruct) : jsonStruct; + } catch (JsonParsingException e) { + throw new InvalidJsonException(e); + } + // not catching a JsonException as it never happens here + } + @Override public String toJson(Object obj) { if (obj instanceof JsonObjectBuilder) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java index fa7dda41..a7fcd711 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JsonProvider.java @@ -15,10 +15,11 @@ package com.jayway.jsonpath.spi.json; import com.jayway.jsonpath.InvalidJsonException; - import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Collection; + public interface JsonProvider { static final Object UNDEFINED = new Object(); @@ -31,6 +32,15 @@ public interface JsonProvider { */ Object parse(String json) throws InvalidJsonException; + /** + * Parse the given json bytes in UTF-8 encoding + * @param json json bytes to parse + * @return Object representation of json + * @throws InvalidJsonException + */ + default Object parse(byte[] json) throws InvalidJsonException { + return parse(new String(json, StandardCharsets.UTF_8)); + } /** * Parse the given json string * @param jsonStream input stream to parse diff --git a/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java index ec9b1810..4b97be9f 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.MappingException; +import java.nio.charset.StandardCharsets; import org.junit.Test; import java.io.IOException; @@ -53,6 +54,13 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest { assertThat(node.get("string-property").asText()).isEqualTo("string-value"); } + @Test + public void bytes_json_can_be_parsed() { + ObjectNode node = using(JACKSON_JSON_NODE_CONFIGURATION).parseUtf8(JSON_DOCUMENT.getBytes(StandardCharsets.UTF_8)) + .read("$"); + assertThat(node.get("string-property").asText()).isEqualTo("string-value"); + } + @Test public void always_return_same_object() { // Test because of Bug #211 DocumentContext context = using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON_DOCUMENT); diff --git a/json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java b/json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java index ef79e22a..83cfe5b5 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java @@ -1,12 +1,13 @@ package com.jayway.jsonpath; -import org.junit.Test; - import java.util.Date; +import org.junit.Test; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; + public class JacksonTest extends BaseTest { @Test @@ -25,6 +26,12 @@ public class JacksonTest extends BaseTest { assertThat(fooBarBaz.bar).isEqualTo(10L); assertThat(fooBarBaz.baz).isEqualTo(true); + fooBarBaz = JsonPath.using(JACKSON_CONFIGURATION).parseUtf8(json.getBytes(UTF_8)) + .read("$", FooBarBaz.class); + + assertThat(fooBarBaz.foo).isEqualTo("foo"); + assertThat(fooBarBaz.bar).isEqualTo(10L); + assertThat(fooBarBaz.baz).isEqualTo(true); } public static class FooBarBaz { @@ -52,7 +59,11 @@ public class JacksonTest extends BaseTest { final Object readFromSingleQuote = JsonPath.using(JACKSON_CONFIGURATION).parse(jsonArray).read("$.[?(@.foo in ['bar'])].foo"); final Object readFromDoubleQuote = JsonPath.using(JACKSON_CONFIGURATION).parse(jsonArray).read("$.[?(@.foo in [\"bar\"])].foo"); assertThat(readFromSingleQuote).isEqualTo(readFromDoubleQuote); - + final Object readFromSingleQuoteBytes = JsonPath.using(JACKSON_CONFIGURATION).parseUtf8(jsonArray.getBytes(UTF_8)) + .read("$.[?(@.foo in ['bar'])].foo"); + final Object readFromDoubleQuoteBytes = JsonPath.using(JACKSON_CONFIGURATION).parseUtf8(jsonArray.getBytes(UTF_8)) + .read("$.[?(@.foo in [\"bar\"])].foo"); + assertThat(readFromSingleQuoteBytes).isEqualTo(readFromDoubleQuoteBytes); } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/JakartaJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/JakartaJsonProviderTest.java index c7ecd0d6..967c2957 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JakartaJsonProviderTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JakartaJsonProviderTest.java @@ -1,20 +1,20 @@ package com.jayway.jsonpath; -import org.junit.Test; - import jakarta.json.JsonObject; import jakarta.json.JsonString; - import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.Test; import static com.jayway.jsonpath.JsonPath.parse; import static com.jayway.jsonpath.JsonPath.using; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; + public class JakartaJsonProviderTest extends BaseTest { private static final Map EMPTY_MAP = emptyMap(); @@ -28,6 +28,15 @@ public class JakartaJsonProviderTest extends BaseTest { assertThat(((JsonString) book.get("author")).getChars()).isEqualTo("Nigel Rees"); } + @Test + public void an_object_can_be_read_from_bytes() { + JsonObject book = using(JAKARTA_JSON_CONFIGURATION) + .parseUtf8(JSON_DOCUMENT.getBytes(UTF_8)) + .read("$.store.book[0]"); + + assertThat(((JsonString) book.get("author")).getChars()).isEqualTo("Nigel Rees"); + } + @Test public void a_property_can_be_read() { JsonString category = using(JAKARTA_JSON_CONFIGURATION)