Skip to content

Commit

Permalink
expose JSON providers ability to parse UTF-8 byte arrays (#770)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardstartin authored Jan 30, 2022
1 parent df9cfd2 commit 921d3bc
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 43 deletions.
3 changes: 3 additions & 0 deletions json-path/src/main/java/com/jayway/jsonpath/ParseContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.InputStream;
import java.net.URL;


/**
* Parses JSON as specified by the used {@link com.jayway.jsonpath.spi.json.JsonProvider}.
*/
Expand All @@ -34,6 +35,8 @@ public interface ParseContext {

DocumentContext parse(File json) throws IOException;

DocumentContext parseUtf8(byte[] json);

@Deprecated
DocumentContext parse(URL json) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ public DocumentContext parse(String json) {
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");
Expand Down
20 changes: 20 additions & 0 deletions json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,26 @@ public static <T extends CharSequence> T notEmpty(T chars, String message) {
return chars;
}

/**
* <p>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.
* <p/>
* <pre>Validate.notEmpty(myString, "The string must not be empty");</pre>
*
* @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;
}

/**
* <p>Validate that the specified argument character sequence is
* neither {@code null} nor a length of zero (no characters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@
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();
Expand Down Expand Up @@ -54,6 +55,16 @@ public Object parse(String json) throws InvalidJsonException {
}
}

@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
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();
Expand Down Expand Up @@ -73,6 +74,16 @@ public Object parse(String json) throws InvalidJsonException {
}
}

@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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
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;
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;
Expand All @@ -19,22 +35,6 @@
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 {

Expand Down Expand Up @@ -69,34 +69,34 @@ public JakartaJsonProvider(boolean mutableJson) {

@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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
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;
Expand Down Expand Up @@ -53,6 +54,13 @@ public void json_can_be_parsed() {
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);
Expand Down
17 changes: 14 additions & 3 deletions json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -25,6 +26,12 @@ public void an_object_can_be_mapped_to_pojo() {
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 {
Expand Down Expand Up @@ -52,7 +59,11 @@ public void single_quotes_work_with_in_filter() {
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);
}

}
Original file line number Diff line number Diff line change
@@ -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<String, Object> EMPTY_MAP = emptyMap();
Expand All @@ -28,6 +28,15 @@ public void an_object_can_be_read() {
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)
Expand Down

0 comments on commit 921d3bc

Please sign in to comment.