/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you 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 | |
* | |
* http://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 org.apache.johnzon.jsonb; | |
import static org.junit.Assert.assertEquals; | |
import java.lang.reflect.Type; | |
import java.util.Arrays; | |
import java.util.Objects; | |
import java.util.UUID; | |
import java.util.stream.Collectors; | |
import javax.json.JsonString; | |
import javax.json.JsonValue; | |
import javax.json.bind.Jsonb; | |
import javax.json.bind.JsonbBuilder; | |
import javax.json.bind.adapter.JsonbAdapter; | |
import javax.json.bind.annotation.JsonbTypeAdapter; | |
import javax.json.bind.annotation.JsonbTypeDeserializer; | |
import javax.json.bind.annotation.JsonbTypeSerializer; | |
import javax.json.bind.serializer.DeserializationContext; | |
import javax.json.bind.serializer.JsonbDeserializer; | |
import javax.json.bind.serializer.JsonbSerializer; | |
import javax.json.bind.serializer.SerializationContext; | |
import javax.json.stream.JsonGenerator; | |
import javax.json.stream.JsonParser; | |
import org.junit.Test; | |
public class SerializersRoundTripTest { | |
public enum Color { | |
RED, GREEN, BLUE | |
} | |
// Does not seem to work with enums | |
public static class Option { | |
public static final Option YES = new Option(true); | |
public static final Option NO = new Option(false); | |
private final boolean value; | |
private Option(boolean value) { | |
this.value = value; | |
} | |
public boolean asBoolean() { | |
return value; | |
} | |
public static Option of(boolean value) { | |
return value ? YES : NO; | |
} | |
@Override | |
public String toString() { | |
return "Option{value=" + value + '}'; | |
} | |
@Override | |
public boolean equals(final Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (o == null || getClass() != o.getClass()) { | |
return false; | |
} | |
Option option = (Option) o; | |
return value == option.value; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(value); | |
} | |
} | |
public static class VATNumber { | |
private final long value; | |
public VATNumber(long value) { | |
this.value = value; | |
} | |
public long getValue() { | |
return value; | |
} | |
@Override | |
public String toString() { | |
return "VATNumber{value=" + value + '}'; | |
} | |
@Override | |
public boolean equals(final Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (o == null || getClass() != o.getClass()) { | |
return false; | |
} | |
VATNumber vatNumber = (VATNumber) o; | |
return value == vatNumber.value; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(value); | |
} | |
} | |
public interface Composite<T, X> extends JsonbSerializer<T>, JsonbDeserializer<T>, JsonbAdapter<T, X> {} | |
public static abstract class AbstractComposite<T, X> implements Composite<T, X> {} | |
public static abstract class StringValueComposite<T> extends AbstractComposite<T, String> {} | |
public static class UUIDComposite extends StringValueComposite<UUID> { | |
public void serialize(UUID obj, JsonGenerator generator, SerializationContext ctx) { | |
generator.write(obj.toString()); | |
} | |
public UUID deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { | |
return UUID.fromString(parser.getString()); | |
} | |
@Override | |
public UUID adaptFromJson(String obj) throws Exception { | |
return UUID.fromString(obj); | |
} | |
@Override | |
public String adaptToJson(UUID obj) throws Exception { | |
return obj.toString(); | |
} | |
} | |
public static class OptionDeSer implements JsonbSerializer<Option>, JsonbDeserializer<Option> { | |
@Override | |
public Option deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { | |
return Option.of(parser.getValue().equals(JsonValue.TRUE)); | |
} | |
@Override | |
public void serialize(Option obj, JsonGenerator generator, SerializationContext ctx) { | |
generator.write(obj.asBoolean()); | |
} | |
} | |
public static class VATDeSer implements JsonbSerializer<VATNumber>, JsonbDeserializer<VATNumber> { | |
@Override | |
public VATNumber deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { | |
return new VATNumber(parser.getLong()); | |
} | |
@Override | |
public void serialize(VATNumber obj, JsonGenerator generator, SerializationContext ctx) { | |
generator.write(obj.getValue()); | |
} | |
} | |
public static class CharsDeSer implements JsonbSerializer<String>, JsonbDeserializer<String> { | |
@Override | |
public String deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { | |
return parser.getArrayStream().map(JsonString.class::cast).map(JsonString::getString).collect(Collectors.joining()); | |
} | |
@Override | |
public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) { | |
generator.writeStartArray(); | |
obj.chars().forEach(c -> generator.write(Character.toString((char) c))); | |
generator.writeEnd(); | |
} | |
} | |
public static class ColorDeSer implements JsonbSerializer<Color>, JsonbDeserializer<Color> { | |
@Override | |
public Color deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { | |
switch (parser.getString()) { | |
case "R" : return Color.RED; | |
case "G" : return Color.GREEN; | |
case "B" : return Color.BLUE; | |
default : throw new IllegalArgumentException(); | |
} | |
} | |
@Override | |
public void serialize(Color obj, JsonGenerator generator, SerializationContext ctx) { | |
generator.write(obj.name().substring(0, 1)); | |
} | |
} | |
public static class Wrapper { | |
@JsonbTypeSerializer(UUIDComposite.class) | |
@JsonbTypeDeserializer(UUIDComposite.class) | |
public UUID uuid; | |
@JsonbTypeAdapter(UUIDComposite.class) | |
public UUID uuid2; | |
@JsonbTypeSerializer(OptionDeSer.class) | |
@JsonbTypeDeserializer(OptionDeSer.class) | |
public Option option; | |
@JsonbTypeSerializer(VATDeSer.class) | |
@JsonbTypeDeserializer(VATDeSer.class) | |
public VATNumber vatNumber; | |
@JsonbTypeSerializer(CharsDeSer.class) | |
@JsonbTypeDeserializer(CharsDeSer.class) | |
public String hello; | |
@JsonbTypeSerializer(ColorDeSer.class) | |
@JsonbTypeDeserializer(ColorDeSer.class) | |
public Color color; | |
@Override | |
public String toString() { | |
return "Wrapper{uuid=" + uuid + ", uuid2=" + uuid2 + ", option=" + option + | |
", vatNumber=" + vatNumber + ", hello='" + hello + '\'' + ", color=" + color + '}'; | |
} | |
@Override | |
public boolean equals(final Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (o == null || getClass() != o.getClass()) { | |
return false; | |
} | |
final Wrapper wrapper = (Wrapper) o; | |
return Objects.equals(uuid, wrapper.uuid) && Objects.equals(uuid2, wrapper.uuid2) && Objects.equals(option, | |
wrapper.option) && Objects.equals(vatNumber, wrapper.vatNumber) && Objects.equals(hello, | |
wrapper.hello) && color == wrapper.color; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(uuid, uuid2, option, vatNumber, hello, color); | |
} | |
} | |
public static class ArrayContainer { | |
// native types | |
private boolean[] bboolean; | |
private byte[] bbyte; | |
private char[] bchar; | |
private short[] bshort; | |
private int[] bint; | |
private long[] blong; | |
private float[] bfloat; | |
private double[] bdouble; | |
// string[] | |
private String[] bString; | |
// wrapper types | |
private Boolean[] bWboolean; | |
private Byte[] bWbyte; | |
private Character[] bWchar; | |
private Short[] bWshort; | |
private Integer[] bWint; | |
private Long[] bWlong; | |
private Float[] bWfloat; | |
private Double[] bWdouble; | |
public boolean[] getBboolean() { | |
return bboolean; | |
} | |
public void setBboolean(boolean[] bboolean) { | |
this.bboolean = bboolean; | |
} | |
public byte[] getBbyte() { | |
return bbyte; | |
} | |
public void setBbyte(byte[] bbyte) { | |
this.bbyte = bbyte; | |
} | |
public char[] getBchar() { | |
return bchar; | |
} | |
public void setBchar(char[] bchar) { | |
this.bchar = bchar; | |
} | |
public short[] getBshort() { | |
return bshort; | |
} | |
public void setBshort(short[] bshort) { | |
this.bshort = bshort; | |
} | |
public int[] getBint() { | |
return bint; | |
} | |
public void setBint(int[] bint) { | |
this.bint = bint; | |
} | |
public long[] getBlong() { | |
return blong; | |
} | |
public void setBlong(long[] blong) { | |
this.blong = blong; | |
} | |
public float[] getBfloat() { | |
return bfloat; | |
} | |
public void setBfloat(float[] bfloat) { | |
this.bfloat = bfloat; | |
} | |
public double[] getBdouble() { | |
return bdouble; | |
} | |
public void setBdouble(double[] bdouble) { | |
this.bdouble = bdouble; | |
} | |
public String[] getbString() { | |
return bString; | |
} | |
public void setbString(String[] bString) { | |
this.bString = bString; | |
} | |
public Boolean[] getbWboolean() { | |
return bWboolean; | |
} | |
public void setbWboolean(Boolean[] bWboolean) { | |
this.bWboolean = bWboolean; | |
} | |
public Byte[] getbWbyte() { | |
return bWbyte; | |
} | |
public void setbWbyte(Byte[] bWbyte) { | |
this.bWbyte = bWbyte; | |
} | |
public Character[] getbWchar() { | |
return bWchar; | |
} | |
public void setbWchar(Character[] bWchar) { | |
this.bWchar = bWchar; | |
} | |
public Short[] getbWshort() { | |
return bWshort; | |
} | |
public void setbWshort(Short[] bWshort) { | |
this.bWshort = bWshort; | |
} | |
public Integer[] getbWint() { | |
return bWint; | |
} | |
public void setbWint(Integer[] bWint) { | |
this.bWint = bWint; | |
} | |
public Long[] getbWlong() { | |
return bWlong; | |
} | |
public void setbWlong(Long[] bWlong) { | |
this.bWlong = bWlong; | |
} | |
public Float[] getbWfloat() { | |
return bWfloat; | |
} | |
public void setbWfloat(Float[] bWfloat) { | |
this.bWfloat = bWfloat; | |
} | |
public Double[] getbWdouble() { | |
return bWdouble; | |
} | |
public void setbWdouble(Double[] bWdouble) { | |
this.bWdouble = bWdouble; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (o == null || getClass() != o.getClass()) { | |
return false; | |
} | |
ArrayContainer that = (ArrayContainer) o; | |
if (!Arrays.equals(bboolean, that.bboolean)) { | |
return false; | |
} | |
if (!Arrays.equals(bbyte, that.bbyte)) { | |
return false; | |
} | |
if (!Arrays.equals(bchar, that.bchar)) { | |
return false; | |
} | |
if (!Arrays.equals(bshort, that.bshort)) { | |
return false; | |
} | |
if (!Arrays.equals(bint, that.bint)) { | |
return false; | |
} | |
if (!Arrays.equals(blong, that.blong)) { | |
return false; | |
} | |
if (!Arrays.equals(bfloat, that.bfloat)) { | |
return false; | |
} | |
if (!Arrays.equals(bdouble, that.bdouble)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bString, that.bString)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWboolean, that.bWboolean)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWbyte, that.bWbyte)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWchar, that.bWchar)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWshort, that.bWshort)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWint, that.bWint)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWlong, that.bWlong)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
if (!Arrays.equals(bWfloat, that.bWfloat)) { | |
return false; | |
} | |
// Probably incorrect - comparing Object[] arrays with Arrays.equals | |
return Arrays.equals(bWdouble, that.bWdouble); | |
} | |
@Override | |
public int hashCode() { | |
int result = Arrays.hashCode(bboolean); | |
result = 31 * result + Arrays.hashCode(bbyte); | |
result = 31 * result + Arrays.hashCode(bchar); | |
result = 31 * result + Arrays.hashCode(bshort); | |
result = 31 * result + Arrays.hashCode(bint); | |
result = 31 * result + Arrays.hashCode(blong); | |
result = 31 * result + Arrays.hashCode(bfloat); | |
result = 31 * result + Arrays.hashCode(bdouble); | |
result = 31 * result + Arrays.hashCode(bString); | |
result = 31 * result + Arrays.hashCode(bWboolean); | |
result = 31 * result + Arrays.hashCode(bWbyte); | |
result = 31 * result + Arrays.hashCode(bWchar); | |
result = 31 * result + Arrays.hashCode(bWshort); | |
result = 31 * result + Arrays.hashCode(bWint); | |
result = 31 * result + Arrays.hashCode(bWlong); | |
result = 31 * result + Arrays.hashCode(bWfloat); | |
result = 31 * result + Arrays.hashCode(bWdouble); | |
return result; | |
} | |
} | |
@Test | |
public void testArrayRoundTrip() throws Exception { | |
ArrayContainer original = new ArrayContainer(); | |
original.setBboolean(new boolean[]{true, false, true}); | |
original.setbWboolean(new Boolean[]{true, false, true}); | |
original.setBbyte(new byte[]{0x00, 0x01, 0x02}); | |
original.setbWbyte(new Byte[]{0x00, 0x01, 0x02}); | |
original.setBchar(new char[]{'a','b', 'c'}); | |
original.setbWchar(new Character[]{'a','b', 'c'}); | |
original.setBshort(new short[]{0, 1, 2}); | |
original.setbWshort(new Short[]{0, 1, 2}); | |
original.setBint(new int[]{0, 1, 2}); | |
original.setbWint(new Integer[]{0, 1, 2}); | |
original.setBlong(new long[]{0L, 1L, 2L}); | |
original.setbWlong(new Long[]{0L, 1L, 2L}); | |
original.setBfloat(new float[]{0f, 1f, 2f}); | |
original.setbWfloat(new Float[]{0f, 1f, 2f}); | |
original.setBdouble(new double[]{0d, 1d, 2d}); | |
original.setbWdouble(new Double[]{0d, 1d, 2d}); | |
try (final Jsonb jsonb = JsonbBuilder.create()) { | |
final ArrayContainer deserialized = jsonb.fromJson(jsonb.toJson(original), ArrayContainer.class); | |
assertEquals(original, deserialized); | |
} | |
} | |
@Test | |
public void roundTrip() throws Exception { | |
final Wrapper original = new Wrapper(); | |
original.hello = "hello world"; | |
/*original.uuid = UUID.randomUUID(); | |
original.uuid2 = UUID.randomUUID(); | |
original.option = Option.YES; | |
original.vatNumber = new VATNumber(42); | |
original.color = Color.GREEN;*/ | |
try (final Jsonb jsonb = JsonbBuilder.create()) { | |
final Wrapper deserialized = jsonb.fromJson(jsonb.toJson(original), Wrapper.class); | |
assertEquals(original, deserialized); | |
} | |
} | |
} |