使用 GSON 时将 json NaN 序列化为 null
Serialize json NaN as null when using GSON
是否可以使用 GSON 将 Double NaN 序列化为 null?
我目前遇到的问题是,当我发布 Double NaN 时,JavaScript 订阅者无法反序列化 json 消息。
class Value {
Double value;
}
@Test
public void name() {
Value newValue = new Value();
newValue.value = Double.NaN;
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
String string = gson.toJson(newValue);
System.out.println(string);
}
以上代码打印:
{"value":NaN}
我愿意:
{"value":null}
仅创建覆盖 Float
和 Double
类型适配器的专用类型适配器工厂,并检查可序列化的值是否为 NaN 或无穷大。
public final class NumberOnlyTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory instance = new NumberOnlyTypeAdapterFactory();
private static final TypeToken<Float> floatTypeToken = TypeToken.get(Float.class);
private static final TypeToken<Double> doubleTypeToken = TypeToken.get(Double.class);
private NumberOnlyTypeAdapterFactory() {
}
public static TypeAdapterFactory getInstance() {
return instance;
}
@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawType = typeToken.getRawType();
final TypeAdapter<? extends Number> typeAdapter;
if ( rawType == Float.class ) {
typeAdapter = NumberOnlyTypeAdapter.create(gson.getDelegateAdapter(this, floatTypeToken), v -> Float.isNaN(v), v -> Float.isInfinite(v));
} else if ( rawType == Double.class ) {
typeAdapter = NumberOnlyTypeAdapter.create(gson.getDelegateAdapter(this, doubleTypeToken), v -> Double.isNaN(v), v -> Double.isInfinite(v));
} else {
return null;
}
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
return castTypeAdapter;
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
private static final class NumberOnlyTypeAdapter<T extends Number>
extends TypeAdapter<T> {
private final TypeAdapter<? super T> delegate;
private final Predicate<? super T> isNan;
private final Predicate<? super T> isInfinite;
private static <T extends Number> TypeAdapter<T> create(final TypeAdapter<? super T> delegate, final Predicate<? super T> isNan,
final Predicate<? super T> isInfinite) {
return new NumberOnlyTypeAdapter<T>(delegate, isNan, isInfinite)
.nullSafe();
}
@Override
public void write(final JsonWriter jsonWriter, final T value)
throws IOException {
if ( !isNan.test(value) && !isInfinite.test(value) ) {
delegate.write(jsonWriter, value);
return;
}
jsonWriter.nullValue();
}
@Override
public T read(final JsonReader jsonReader) {
throw new UnsupportedOperationException("TODO");
}
}
}
这是一个测试(原始数组用于测试原始浮点数和双精度数,而原始值分别装箱为 Float
和 Double
):
public final class NumberOnlyTypeAdapterFactoryTest {
private static final Gson gson = new GsonBuilder()
.disableInnerClassSerialization()
.disableHtmlEscaping()
.registerTypeAdapterFactory(NumberOnlyTypeAdapterFactory.getInstance())
.create();
private static Stream<Arguments> test() {
return Stream.of(
Arguments.of(Float.class, 0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, (Function<Float, float[]>) v -> new float[]{ v }),
Arguments.of(Double.class, 0D, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, (Function<Double, double[]>) v -> new double[]{ v })
);
}
@ParameterizedTest
@MethodSource
public <N extends Number, A> void test(
final Type type,
final N zeroValue,
final N nanValue,
final N negativeInfinityValue,
final N positiveInfinityValue,
final Function<? super N, ? extends A> convertToPrimitiveArray
) {
Assertions.assertEquals("0.0", gson.toJson(zeroValue));
Assertions.assertEquals("null", gson.toJson(nanValue));
Assertions.assertEquals("null", gson.toJson(negativeInfinityValue));
Assertions.assertEquals("null", gson.toJson(positiveInfinityValue));
Assertions.assertEquals("null", gson.toJson(null, type));
Assertions.assertEquals("[0.0]", gson.toJson(convertToPrimitiveArray.apply(zeroValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(nanValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(negativeInfinityValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(positiveInfinityValue)));
}
}
是否可以使用 GSON 将 Double NaN 序列化为 null?
我目前遇到的问题是,当我发布 Double NaN 时,JavaScript 订阅者无法反序列化 json 消息。
class Value {
Double value;
}
@Test
public void name() {
Value newValue = new Value();
newValue.value = Double.NaN;
Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
String string = gson.toJson(newValue);
System.out.println(string);
}
以上代码打印:
{"value":NaN}
我愿意:
{"value":null}
仅创建覆盖 Float
和 Double
类型适配器的专用类型适配器工厂,并检查可序列化的值是否为 NaN 或无穷大。
public final class NumberOnlyTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory instance = new NumberOnlyTypeAdapterFactory();
private static final TypeToken<Float> floatTypeToken = TypeToken.get(Float.class);
private static final TypeToken<Double> doubleTypeToken = TypeToken.get(Double.class);
private NumberOnlyTypeAdapterFactory() {
}
public static TypeAdapterFactory getInstance() {
return instance;
}
@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawType = typeToken.getRawType();
final TypeAdapter<? extends Number> typeAdapter;
if ( rawType == Float.class ) {
typeAdapter = NumberOnlyTypeAdapter.create(gson.getDelegateAdapter(this, floatTypeToken), v -> Float.isNaN(v), v -> Float.isInfinite(v));
} else if ( rawType == Double.class ) {
typeAdapter = NumberOnlyTypeAdapter.create(gson.getDelegateAdapter(this, doubleTypeToken), v -> Double.isNaN(v), v -> Double.isInfinite(v));
} else {
return null;
}
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
return castTypeAdapter;
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
private static final class NumberOnlyTypeAdapter<T extends Number>
extends TypeAdapter<T> {
private final TypeAdapter<? super T> delegate;
private final Predicate<? super T> isNan;
private final Predicate<? super T> isInfinite;
private static <T extends Number> TypeAdapter<T> create(final TypeAdapter<? super T> delegate, final Predicate<? super T> isNan,
final Predicate<? super T> isInfinite) {
return new NumberOnlyTypeAdapter<T>(delegate, isNan, isInfinite)
.nullSafe();
}
@Override
public void write(final JsonWriter jsonWriter, final T value)
throws IOException {
if ( !isNan.test(value) && !isInfinite.test(value) ) {
delegate.write(jsonWriter, value);
return;
}
jsonWriter.nullValue();
}
@Override
public T read(final JsonReader jsonReader) {
throw new UnsupportedOperationException("TODO");
}
}
}
这是一个测试(原始数组用于测试原始浮点数和双精度数,而原始值分别装箱为 Float
和 Double
):
public final class NumberOnlyTypeAdapterFactoryTest {
private static final Gson gson = new GsonBuilder()
.disableInnerClassSerialization()
.disableHtmlEscaping()
.registerTypeAdapterFactory(NumberOnlyTypeAdapterFactory.getInstance())
.create();
private static Stream<Arguments> test() {
return Stream.of(
Arguments.of(Float.class, 0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, (Function<Float, float[]>) v -> new float[]{ v }),
Arguments.of(Double.class, 0D, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, (Function<Double, double[]>) v -> new double[]{ v })
);
}
@ParameterizedTest
@MethodSource
public <N extends Number, A> void test(
final Type type,
final N zeroValue,
final N nanValue,
final N negativeInfinityValue,
final N positiveInfinityValue,
final Function<? super N, ? extends A> convertToPrimitiveArray
) {
Assertions.assertEquals("0.0", gson.toJson(zeroValue));
Assertions.assertEquals("null", gson.toJson(nanValue));
Assertions.assertEquals("null", gson.toJson(negativeInfinityValue));
Assertions.assertEquals("null", gson.toJson(positiveInfinityValue));
Assertions.assertEquals("null", gson.toJson(null, type));
Assertions.assertEquals("[0.0]", gson.toJson(convertToPrimitiveArray.apply(zeroValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(nanValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(negativeInfinityValue)));
Assertions.assertEquals("[null]", gson.toJson(convertToPrimitiveArray.apply(positiveInfinityValue)));
}
}