Skip to content

Commit

Permalink
Support unwrapping arrays for JsonNode decoder (#747)
Browse files Browse the repository at this point in the history
* Support unwrapping arrays for `JsonNode` decoder

* Add since

* Remove unneeded methods

* Update serde-oracle-jdbc-json/src/main/java/io/micronaut/serde/oracle/jdbc/json/AbstractOracleJdbcJsonObjectMapper.java

Co-authored-by: Jonas Konrad <jonas.konrad@oracle.com>

---------

Co-authored-by: Jonas Konrad <jonas.konrad@oracle.com>
  • Loading branch information
dstepanov and yawkat authored Feb 1, 2024
1 parent 503b1de commit cf00261
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 1 deletion.
1 change: 1 addition & 0 deletions serde-jsonp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
testCompileOnly(mn.micronaut.inject.groovy)
testImplementation(mnTest.micronaut.test.spock)
testImplementation(mnReactor.micronaut.reactor)
testRuntimeOnly("org.yaml:snakeyaml")
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.micronaut.serde.json.stream.nodes;

import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.convert.format.MapFormat;
import io.micronaut.core.naming.conventions.StringConvention;
import io.micronaut.serde.annotation.Serdeable;

import java.util.List;
import java.util.Map;

@ConfigurationProperties("example.root.config")
@Serdeable
public class RootConfig {
private String someValue;
private NestedConfig someNested = new NestedConfig();
private List<NestedListConfig> someListNested = List.of();
@MapFormat(keyFormat = StringConvention.RAW)
private Map<String, RawNestedConfig> someRawNested = Map.of();

public String getSomeValue() {
return someValue;
}

public void setSomeValue(String someValue) {
this.someValue = someValue;
}

public NestedConfig getSomeNested() {
return someNested;
}

public void setSomeNested(NestedConfig someNested) {
this.someNested = someNested;
}

public List<NestedListConfig> getSomeListNested() {
return someListNested;
}

public void setSomeListNested(List<NestedListConfig> someListNested) {
this.someListNested = someListNested;
}

public Map<String, RawNestedConfig> getSomeRawNested() {
return someRawNested;
}

public void setSomeRawNested(Map<String, RawNestedConfig> someRawNested) {
this.someRawNested = someRawNested;
}

@Serdeable
@ConfigurationProperties("some-nested")
public static class NestedConfig {
private String nestedValue;

public String getNestedValue() {
return nestedValue;
}

public void setNestedValue(String nestedValue) {
this.nestedValue = nestedValue;
}
}

@Serdeable
public static class NestedListConfig {
private String nestedListValue;

public String getNestedListValue() {
return nestedListValue;
}

public void setNestedListValue(String nestedListValue) {
this.nestedListValue = nestedListValue;
}
}

@Serdeable
public static class RawNestedConfig {
private String rawNestedValue;

public String getRawNestedValue() {
return rawNestedValue;
}

public void setRawNestedValue(String rawNestedValue) {
this.rawNestedValue = rawNestedValue;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.micronaut.serde.json.stream.nodes

import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification

@MicronautTest
class RootConfigSpec extends Specification {

@Inject
RootConfig rootConfig

void "test config injection"() {
expect:
rootConfig.someValue == "foo"
rootConfig.someNested
rootConfig.someNested.nestedValue == "bar"
rootConfig.someListNested
rootConfig.someListNested.size() == 2
rootConfig.someListNested[0].nestedListValue == "baz1"
rootConfig.someListNested[1].nestedListValue == "baz2"
rootConfig.someRawNested
rootConfig.someRawNested.size() == 2
rootConfig.someRawNested.get('abc_def').rawNestedValue == "abc123"
rootConfig.someRawNested.get('def_ghi').rawNestedValue == "def456"
}
}
14 changes: 14 additions & 0 deletions serde-jsonp/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
example:
root:
config:
some-value: "foo"
some-nested:
nested-value: "bar"
some-list-nested:
- nestedListValue: "baz1"
- nestedListValue: "baz2"
some-raw-nested:
abc_def:
raw-nested-value: "abc123"
def_ghi:
raw-nested-value: "def456"
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected BigDecimal getBigDecimal() {
@Override
protected Number getBestNumber() {
return switch (currentEvent) {
case VALUE_DECIMAL -> jsonParser.getLong();
case VALUE_DECIMAL -> jsonParser.getBigDecimal();
case VALUE_DOUBLE -> jsonParser.getDouble();
case VALUE_FLOAT -> jsonParser.getFloat();
default ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ public String decodeString() throws IOException {
if (peeked.isString()) {
skipValue();
return peeked.getStringValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.STRING)) {
String unwrapped = decoder.decodeString();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one string, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a string", toArbitrary(peeked));
}
Expand All @@ -88,6 +97,15 @@ public boolean decodeBoolean() throws IOException {
if (peeked.isBoolean()) {
skipValue();
return peeked.getBooleanValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.BOOLEAN)) {
boolean unwrapped = decoder.decodeBoolean();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one boolean, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a boolean", toArbitrary(peeked));
}
Expand All @@ -99,6 +117,15 @@ public byte decodeByte() throws IOException {
if (peeked.isNumber()) {
skipValue();
return (byte) peeked.getIntValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.BYTE)) {
byte unwrapped = decoder.decodeByte();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one byte, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -110,6 +137,15 @@ public short decodeShort() throws IOException {
if (peeked.isNumber()) {
skipValue();
return (short) peeked.getIntValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.SHORT)) {
short unwrapped = decoder.decodeShort();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one short, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -121,6 +157,15 @@ public char decodeChar() throws IOException {
if (peeked.isNumber()) {
skipValue();
return (char) peeked.getIntValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.CHAR)) {
char unwrapped = decoder.decodeChar();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one char, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -132,6 +177,15 @@ public int decodeInt() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getIntValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.INT)) {
int unwrapped = decoder.decodeInt();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one int, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -143,6 +197,15 @@ public long decodeLong() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getLongValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.LONG)) {
long unwrapped = decoder.decodeLong();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one long, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -154,6 +217,15 @@ public float decodeFloat() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getFloatValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.FLOAT)) {
float unwrapped = decoder.decodeFloat();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one float, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -165,6 +237,15 @@ public double decodeDouble() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getDoubleValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray(Argument.DOUBLE)) {
double unwrapped = decoder.decodeDouble();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one double, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -176,6 +257,15 @@ public BigInteger decodeBigInteger() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getBigIntegerValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray()) {
BigInteger unwrapped = decoder.decodeBigInteger();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one BigInteger, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand All @@ -187,6 +277,15 @@ public BigDecimal decodeBigDecimal() throws IOException {
if (peeked.isNumber()) {
skipValue();
return peeked.getBigDecimalValue();
} else if (peeked.isArray()) {
try (Decoder decoder = decodeArray()) {
BigDecimal unwrapped = decoder.decodeBigDecimal();
if (decoder.hasNextArrayValue()) {
throw createDeserializationException("Expected one BigDecimal, but got array of multiple values", null);
} else {
return unwrapped;
}
}
} else {
throw createDeserializationException("Not a number", toArbitrary(peeked));
}
Expand Down
Loading

0 comments on commit cf00261

Please sign in to comment.