Skip to content

Commit

Permalink
Handle null values
Browse files Browse the repository at this point in the history
  • Loading branch information
tishun committed Aug 28, 2024
1 parent 69d843c commit 57390e8
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 29 deletions.
44 changes: 16 additions & 28 deletions src/main/java/io/lettuce/core/json/DefaultJsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,11 @@
* All rights reserved.
*
* Licensed under the MIT License.
*
* This file contains contributions from third-party contributors
* licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.lettuce.core.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.codec.RedisCodec;
Expand Down Expand Up @@ -69,15 +57,10 @@ private JsonValue<K, V> parse(String value) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode root = mapper.readTree(value);

if (root.isObject()) {
return new DelegateJsonObject<>(root, codec);
} else if (root.isArray()) {
return new DelegateJsonArray<>(root, codec);
}
return new DelegateJsonValue<>(root, codec);
} catch (IOException e) {
throw new RuntimeException(e);
return wrap(root);
} catch (JsonProcessingException e) {
throw new RedisJsonException(
"Failed to process the provided value as JSON: " + String.format("%.50s", value) + "...", e);
}
}

Expand All @@ -89,15 +72,20 @@ public JsonValue<K, V> parse(ByteBuffer byteBuffer) {
byteBuffer.get(bytes);
JsonNode root = mapper.readTree(bytes);

if (root.isObject()) {
return new DelegateJsonObject<>(root, codec);
} else if (root.isArray()) {
return new DelegateJsonArray<>(root, codec);
}
return new DelegateJsonValue<>(root, codec);
return wrap(root);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private JsonValue<K, V> wrap(JsonNode root) {
if (root.isObject()) {
return new DelegateJsonObject<>(root, codec);
} else if (root.isArray()) {
return new DelegateJsonArray<>(root, codec);
}

return new DelegateJsonValue<>(root, codec);
}

}
5 changes: 5 additions & 0 deletions src/main/java/io/lettuce/core/json/DelegateJsonArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,9 @@ public Number asNumber() {
throw new UnsupportedOperationException("The JSON value is not a number");
}

@Override
public boolean isNull() {
return false;
}

}
5 changes: 5 additions & 0 deletions src/main/java/io/lettuce/core/json/DelegateJsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,9 @@ public Number asNumber() {
throw new UnsupportedOperationException("The JSON value is not a number");
}

@Override
public boolean isNull() {
return false;
}

}
4 changes: 4 additions & 0 deletions src/main/java/io/lettuce/core/json/DelegateJsonValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public boolean isNumber() {
return node.isNumber();
}

public boolean isNull() {
return node.isNull();
}

@Override
public Number asNumber() {
if (node.isInt()) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/lettuce/core/json/JsonValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,9 @@ public interface JsonValue<K, V> {
*/
Number asNumber();

/**
* @return {@code true} if this {@link JsonValue} represents the value of null
*/
boolean isNull();

}
37 changes: 37 additions & 0 deletions src/main/java/io/lettuce/core/json/RedisJsonException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2024, Redis Ltd. and Contributors
* All rights reserved.
*
* Licensed under the MIT License.
*
* This file contains contributions from third-party contributors
* licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.lettuce.core.json;

public class RedisJsonException extends RuntimeException {

public RedisJsonException(String message) {
super(message);
}

public RedisJsonException(String message, Throwable cause) {
super(message, cause);
}

public RedisJsonException(Throwable cause) {
super(cause);
}

}
6 changes: 6 additions & 0 deletions src/main/java/io/lettuce/core/json/UnproccessedJsonValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ public Number asNumber() {
return jsonValue.asNumber();
}

@Override
public boolean isNull() {
lazilyDeserialize();
return jsonValue.isNull();
}

private void lazilyDeserialize() {
if (deserialized) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ void jsonGet(String path) {
assertThat(value.get(0).asJsonArray().asList().get(0).isString()).isTrue();
assertThat(value.get(0).asJsonArray().asList().get(0).asString()).isEqualTo("Phoebe");
assertThat(value.get(0).asJsonArray().asList().get(1).isString()).isTrue();
assertThat(value.get(0).asJsonArray().asList().get(1).isNull()).isFalse();
assertThat(value.get(0).asJsonArray().asList().get(1).asString()).isEqualTo("Quaoar");
} else {
assertThat(value.get(0).toValue()).isEqualTo("\"Phoebe\"");
Expand All @@ -184,6 +185,23 @@ void jsonGet(String path) {
}
}

@Test
void jsonGetNull() {
JsonPath myPath = JsonPath.of("$..inventory.owner");

// Verify codec parsing
List<JsonValue<String, String>> value = redis.jsonGet(BIKES_INVENTORY, JsonGetArgs.Builder.none(), myPath);
assertThat(value).hasSize(1);

assertThat(value.get(0).toValue()).isEqualTo("[null]");

// Verify array parsing
assertThat(value.get(0).isJsonArray()).isTrue();
assertThat(value.get(0).asJsonArray().size()).isEqualTo(1);
assertThat(value.get(0).asJsonArray().asList().get(0).toValue()).isEqualTo("null");
assertThat(value.get(0).asJsonArray().asList().get(0).isNull()).isTrue();
}

@ParameterizedTest(name = "With {0} as path")
@ValueSource(strings = { MOUNTAIN_BIKES_V1 + "[1]", MOUNTAIN_BIKES_V2 + "[1]" })
void jsonMerge(String path) {
Expand Down Expand Up @@ -355,7 +373,7 @@ void jsonSet(String path) {
JsonObject<String, String> bikeSpecs = parser.createEmptyJsonObject();
JsonArray<String, String> bikeColors = parser.createEmptyJsonArray();

bikeSpecs.put("material", parser.createJsonValue("\"composite\""));
bikeSpecs.put("material", parser.createJsonValue("null"));
bikeSpecs.put("weight", parser.createJsonValue("11"));

bikeColors.add(parser.createJsonValue("\"yellow\""));
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/bike-inventory.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"inventory": {
"complete": false,
"owner": null,
"mountain_bikes": [
{
"id": "bike:1",
Expand Down

0 comments on commit 57390e8

Please sign in to comment.