Skip to content

Commit

Permalink
Support decoding Number (#765)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Feb 8, 2024
1 parent b51c197 commit c4012eb
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 3 deletions.
22 changes: 22 additions & 0 deletions serde-api/src/main/java/io/micronaut/serde/Decoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,28 @@ default Character decodeCharNullable() throws IOException {
return decodeNull() ? null : decodeChar();
}

/**
* Decodes a number.
* @return The number
* @throws IOException If an unrecoverable error occurs
* @since 2.9.0
*/
default Number decodeNumber() throws IOException {
return decodeBigDecimal();
}

/**
* Equivalent to {@code decodeNull() ? null : decodeNumber()}.
*
* @return The number
* @throws IOException If an unrecoverable error occurs
* @since 2.9.0
*/
@Nullable
default Number decodeNumberNullable() throws IOException {
return decodeNull() ? null : decodeNumber();
}

/**
* Decodes a int.
* @return The int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ import spock.lang.Unroll

class JsonPropertySpec extends JsonCompileSpec {

@Unroll
void "serde Number"(Number number) {
given:
def context = buildContext('example.Test', '''
package example;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.Nullable;
import java.util.Optional;
@io.micronaut.serde.annotation.Serdeable
@Introspected(accessKind = Introspected.AccessKind.FIELD)
class Test {
public Number foo;
}
''', [:])
beanUnderTest.@foo = number

expect:
jsonMapper.readValue(json, typeUnderTest).@foo == result
writeJson(jsonMapper, beanUnderTest) == json

cleanup:
context.close()

where:
number || result || json
123.456 || 123.456 || '{"foo":123.456}'
Double.valueOf(123.123) || Double.valueOf(123.123) || '{"foo":123.123}'
Float.valueOf(123.123) || Double.valueOf(123.123) || '{"foo":123.123}'
BigDecimal.valueOf(123.123) || BigDecimal.valueOf(123.123) || '{"foo":123.123}'
Integer.valueOf(123) || Integer.valueOf(123) || '{"foo":123}'
Short.valueOf((short) 123) || Short.valueOf((short) 123) || '{"foo":123}'
BigInteger.valueOf(123) || BigInteger.valueOf(123) || '{"foo":123}'

}

void "missing nullable properties are not overwritten"() {
given:
def context = buildContext('example.Test', '''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -774,11 +774,20 @@ public BigDecimal decodeBigDecimalNullable() throws IOException {
}
}

private Number decodeNumber() throws IOException {
private Number doDecodeNumber() throws IOException {
nextToken();
return parser.getNumberValue();
}

@Override
public Number decodeNumber() throws IOException {
return switch (peekToken()) {
case VALUE_STRING -> decodeBigDecimal();
case VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT -> doDecodeNumber();
default -> throw unexpectedToken(JsonToken.VALUE_NUMBER_INT, nextToken());
};
}

@Override
public byte @NonNull [] decodeBinary() throws IOException {
return switch (peekToken()) {
Expand Down Expand Up @@ -969,7 +978,7 @@ ArbitraryBuilder proceed() throws IOException {
return this;
}
case VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT -> {
put(key, elementDecoder.decodeNumber());
put(key, elementDecoder.doDecodeNumber());
return this;
}
case VALUE_TRUE, VALUE_FALSE -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2017-2024 original authors
*
* 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.micronaut.serde.support.serdes;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.type.Argument;
import io.micronaut.serde.Decoder;
import io.micronaut.serde.Encoder;
import io.micronaut.serde.exceptions.SerdeException;
import io.micronaut.serde.support.SerdeRegistrar;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;

@Internal
final class NumberTypeSerde implements SerdeRegistrar<Number>, NumberSerde<Number> {

@Override
public Argument<Number> getType() {
return Argument.of(Number.class);
}

@Override
public Number deserialize(Decoder decoder,
DecoderContext decoderContext,
Argument<? super Number> type) throws IOException {
return decoder.decodeNumber();
}

@Override
public Number deserializeNullable(@NonNull Decoder decoder, @NonNull DecoderContext context, @NonNull Argument<? super Number> type) throws IOException {
return decoder.decodeNumberNullable();
}

@Override
public void serialize(Encoder encoder,
EncoderContext context,
Argument<? extends Number> type,
Number value) throws IOException {
if (value instanceof Integer integer) {
encoder.encodeInt(integer);
} else if (value instanceof Long aLong) {
encoder.encodeLong(aLong);
} else if (value instanceof Double aDouble) {
encoder.encodeDouble(aDouble);
} else if (value instanceof Float aFloat) {
encoder.encodeFloat(aFloat);
} else if (value instanceof Byte aByte) {
encoder.encodeByte(aByte);
} else if (value instanceof Short aShort) {
encoder.encodeShort(aShort);
} else if (value instanceof BigDecimal bigDecimal) {
encoder.encodeBigDecimal(bigDecimal);
} else if (value instanceof BigInteger bigInteger) {
encoder.encodeBigInteger(bigInteger);
} else {
throw new SerdeException("Unrecognized Number type: " + value.getClass().getName() + " " + value);
}
}

@Nullable
@Override
public Integer getDefaultValue(@NonNull DecoderContext context, @NonNull Argument<? super Number> type) {
return type.isPrimitive() ? 0 : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public final class Serdes {
new PeriodSerde(),
new ByteBufferSerde(),
new StringArraySerde(),
new OptionalSerde<>()
new OptionalSerde<>(),
new NumberTypeSerde()
);

public static void register(SerdeConfiguration serdeConfiguration,
Expand Down

0 comments on commit c4012eb

Please sign in to comment.