Java tutorial
/** * Copyright (C) 2009-2012 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 * * 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.fusesource.restygwt.client.codec; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.ws.rs.GET; import javax.ws.rs.Path; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonValue; import org.fusesource.restygwt.client.AbstractJsonEncoderDecoder; import org.fusesource.restygwt.client.AbstractNestedJsonEncoderDecoder; import org.fusesource.restygwt.client.Defaults; import org.fusesource.restygwt.client.Json; import org.fusesource.restygwt.client.JsonEncoderDecoder; import org.fusesource.restygwt.client.MethodCallback; import org.fusesource.restygwt.client.ObjectEncoderDecoder; import org.fusesource.restygwt.client.RestService; import org.fusesource.restygwt.client.basic.Optional; import org.fusesource.restygwt.client.codec.EncoderDecoderTestGwt.WithEnum.Cycle; import com.google.gwt.core.client.GWT; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import com.google.gwt.junit.client.GWTTestCase; import javax.xml.bind.annotation.XmlTransient; public class EncoderDecoderTestGwt extends GWTTestCase { public interface WrapperLibraryCodec extends JsonEncoderDecoder<LibraryWithWrapper> { } public interface ArrayWrapperLibraryCodec extends JsonEncoderDecoder<LibraryWithArrayWrapper> { } public interface PropertyLibraryCodec extends JsonEncoderDecoder<LibraryWithProperty> { } static class ANumber<T extends Number> { T n; T get() { return n; } } public interface IntegerCodec extends JsonEncoderDecoder<ANumber<Integer>> { } public interface FloatCodec extends JsonEncoderDecoder<ANumber<Float>> { } public static class Name { public String name; } public static class Foo { public List<String> bars = new ArrayList<String>(); public List<Name> names = new ArrayList<Name>(); } public interface FooCodec extends JsonEncoderDecoder<Foo> { } @Override public String getModuleName() { return "org.fusesource.restygwt.EncoderDecoderTestGwt"; } public void testNullPrimitiveValueInList() { FooCodec fooCoder = GWT.create(FooCodec.class); Foo foo = new Foo(); foo.bars.add(null); JSONValue fooJ = fooCoder.encode(foo); assertEquals(foo.bars, fooCoder.decode(fooJ).bars); } public void testNullPojoValueInList() { FooCodec fooCoder = GWT.create(FooCodec.class); Foo foo = new Foo(); foo.names.add(null); JSONValue fooJ = fooCoder.encode(foo); assertEquals(foo.names, fooCoder.decode(fooJ).names); } public void testSubtypeWrappeObjectWithSingleSubtype() { WrapperLibraryCodec lc = GWT.create(WrapperLibraryCodec.class); LibraryWithWrapper l = new LibraryWithWrapper(); ArrayList<LibraryItemWithWrapper> libraryItems = new ArrayList<LibraryItemWithWrapper>(); SpriteBasedItemWithWrapper li = new SpriteBasedItemWithWrapper(); li.id = "1"; li.imageRef = "src.png"; libraryItems.add(li); l.items = libraryItems; JSONValue encode = lc.encode(l); LibraryWithWrapper decode = lc.decode(encode); assertEquals(l, decode); } public void testSubtypeArrayWrappeObjectWithSingleSubtype() { ArrayWrapperLibraryCodec lc = GWT.create(ArrayWrapperLibraryCodec.class); LibraryWithArrayWrapper l = new LibraryWithArrayWrapper(); ArrayList<LibraryItemWithArrayWrapper> libraryItems = new ArrayList<LibraryItemWithArrayWrapper>(); SpriteBasedItemWithArrayWrapper li = new SpriteBasedItemWithArrayWrapper(); li.id = "1"; li.imageRef = "src.png"; libraryItems.add(li); l.items = libraryItems; JSONValue encode = lc.encode(l); LibraryWithArrayWrapper decode = lc.decode(encode); assertEquals(l, decode); } public void testSubtypePropertytWithSingleSubtype() { PropertyLibraryCodec lc = GWT.create(PropertyLibraryCodec.class); LibraryWithProperty l = new LibraryWithProperty(); ArrayList<LibraryItemWithProperty> libraryItems = new ArrayList<LibraryItemWithProperty>(); SpriteBasedItemWithProperty li = new SpriteBasedItemWithProperty(); li.id = "1"; li.imageRef = "src.png"; libraryItems.add(li); l.items = libraryItems; JSONValue encode = lc.encode(l); LibraryWithProperty decode = lc.decode(encode); assertEquals(l, decode); } public void testGenericTypes() { IntegerCodec integerCoder = GWT.create(IntegerCodec.class); FloatCodec floatCoder = GWT.create(FloatCodec.class); ANumber<Integer> intA = new ANumber<Integer>(); intA.n = 123; JSONValue intJ = integerCoder.encode(intA); assertEquals(intA.n, integerCoder.decode(intJ).n); ANumber<Float> floatA = new ANumber<Float>(); floatA.n = 123.456f; JSONValue floatJ = floatCoder.encode(floatA); assertEquals(floatA.n, floatCoder.decode(floatJ).n); } public interface CreatorCodec extends JsonEncoderDecoder<CredentialsWithCreator> { } public void testCreators() { CreatorCodec codec = GWT.create(CreatorCodec.class); CredentialsWithCreator c = new CredentialsWithCreator("email", "password"); c.age = 12; JSONValue cJson = codec.encode(c); CredentialsWithCreator cRoundTrip = codec.decode(cJson); assertEquals("email", cRoundTrip.email); assertEquals("password", cRoundTrip.password); assertEquals(12, cRoundTrip.age); } public void testCreatorsWithNullValue() { CreatorCodec codec = GWT.create(CreatorCodec.class); CredentialsWithCreator c = new CredentialsWithCreator(null, "password"); c.age = 12; JSONValue cJson = codec.encode(c); CredentialsWithCreator cRoundTrip = codec.decode(cJson); assertNull(cRoundTrip.email); assertEquals("password", cRoundTrip.password); assertEquals(12, cRoundTrip.age); } public interface WrapperCodec extends JsonEncoderDecoder<CredentialsWithWrapperObject> { } public interface SubWrapperCodec extends JsonEncoderDecoder<SubCredentialsWithWrapperObject> { } public void testSubtypeWrapperObject() { WrapperCodec codec = GWT.create(WrapperCodec.class); CredentialsWithWrapperObject base = new CredentialsWithWrapperObject(); base.setEmail("email-super"); base.setPassword("password-super"); JSONValue baseJson = codec.encode(base); CredentialsWithWrapperObject baseRoundTrip = codec.decode(baseJson); assertEquals("email-super", baseRoundTrip.email); assertEquals("password-super", baseRoundTrip.password); assertFalse(baseRoundTrip.getClass().equals(SubCredentialsWithWrapperObject.class)); SubCredentialsWithWrapperObject sub = new SubCredentialsWithWrapperObject(); sub.setEmail("email-sub"); sub.setPassword("password-sub"); sub.login = "login-sub"; JSONValue subJson = codec.encode(sub); SubCredentialsWithWrapperObject subRoundTrip = (SubCredentialsWithWrapperObject) codec.decode(subJson); assertEquals("email-sub", subRoundTrip.email); assertEquals("password-sub", subRoundTrip.password); assertEquals("login-sub", subRoundTrip.login); SubWrapperCodec subCodec = GWT.create(SubWrapperCodec.class); sub.setEmail("email-direct"); sub.setPassword("password-direct"); sub.login = "login-direct"; subJson = subCodec.encode(sub); subRoundTrip = subCodec.decode(subJson); assertEquals("email-direct", subRoundTrip.email); assertEquals("password-direct", subRoundTrip.password); assertEquals("login-direct", subRoundTrip.login); } public interface PropertyCodec extends JsonEncoderDecoder<CredentialsWithProperty> { } public interface CredentialsWithJacksonAnnotationsInsideCodec extends JsonEncoderDecoder<CredentialsWithJacksonAnnotationsInside> { } public interface SubPropertyCodec extends JsonEncoderDecoder<SubCredentialsWithProperty> { } public void testSubtypeProperty() { PropertyCodec codec = GWT.create(PropertyCodec.class); CredentialsWithProperty base = new CredentialsWithProperty(); base.setEmail("email-super"); base.setPassword("password-super"); JSONValue baseJson = codec.encode(base); CredentialsWithProperty baseRoundTrip = codec.decode(baseJson); assertEquals("email-super", baseRoundTrip.email); assertEquals("password-super", baseRoundTrip.password); assertFalse(baseRoundTrip.getClass().equals(SubCredentialsWithProperty.class)); SubCredentialsWithProperty sub = new SubCredentialsWithProperty(); sub.setEmail("email-sub"); sub.setPassword("password-sub"); sub.login = "login-sub"; JSONValue subJson = codec.encode(sub); SubCredentialsWithProperty subRoundTrip = (SubCredentialsWithProperty) codec.decode(subJson); assertEquals("email-sub", subRoundTrip.email); assertEquals("password-sub", subRoundTrip.password); assertEquals("login-sub", subRoundTrip.login); SubPropertyCodec subCodec = GWT.create(SubPropertyCodec.class); sub.setEmail("email-direct"); sub.setPassword("password-direct"); sub.login = "login-direct"; subJson = subCodec.encode(sub); subRoundTrip = subCodec.decode(subJson); assertEquals("email-direct", subRoundTrip.email); assertEquals("password-direct", subRoundTrip.password); assertEquals("login-direct", subRoundTrip.login); } public void testJacksonAnnotationInsideProperty() { CredentialsWithJacksonAnnotationsInsideCodec codec = GWT .create(CredentialsWithJacksonAnnotationsInsideCodec.class); CredentialsWithJacksonAnnotationsInside base = new CredentialsWithJacksonAnnotationsInside(); base.setEmail("email-super"); base.setPassword("password-super"); JSONValue baseJson = codec.encode(base); CredentialsWithJacksonAnnotationsInside baseRoundTrip = codec.decode(baseJson); assertEquals("email-super", baseRoundTrip.email); assertEquals("password-super", baseRoundTrip.password); assertEquals(baseRoundTrip.getClass(), CredentialsWithJacksonAnnotationsInside.class); } static class B { BigInteger age; } static interface BigCodec extends JsonEncoderDecoder<B> { } public void testBigIntegers() { BigCodec big = GWT.create(BigCodec.class); B b = new B(); b.age = new BigInteger("1234567890123456789012345678901234567890"); JSONValue bJson = big.encode(b); B bRoundTrip = big.decode(bJson); assertEquals(b.age, bRoundTrip.age); } public void testObjectEncoderDecoder() { { double value = Math.random() * 10000; JSONValue json = ObjectEncoderDecoder.INSTANCE.encode(value); assertEquals(value, ObjectEncoderDecoder.INSTANCE.decode(json)); } { String value = "Fred Flintstone"; JSONValue json = ObjectEncoderDecoder.INSTANCE.encode(value); assertEquals(value, ObjectEncoderDecoder.INSTANCE.decode(json)); } { boolean value = Boolean.TRUE; JSONValue json = ObjectEncoderDecoder.INSTANCE.encode(value); assertEquals(value, ObjectEncoderDecoder.INSTANCE.decode(json)); } { Map<String, Object> value = new HashMap<String, Object>(); value.put("fred", "flintstone"); value.put("shoeSize", 12.0); value.put("geek", true); JSONValue json = ObjectEncoderDecoder.INSTANCE.encode(value); assertEquals(value, ObjectEncoderDecoder.INSTANCE.decode(json)); } { List<Object> value = new ArrayList<Object>(); value.add("Fred Flintstone"); value.add(12.0); value.add(false); JSONValue json = ObjectEncoderDecoder.INSTANCE.encode(value); assertEquals(value, ObjectEncoderDecoder.INSTANCE.decode(json)); } } public void testIntegerToStringDecode() { Integer i = 123; assertEquals(i.toString(), AbstractJsonEncoderDecoder.STRING.decode(AbstractJsonEncoderDecoder.INT.encode(i))); } public void testBooleanToStringDecode() { Boolean b = true; assertEquals(b.toString(), AbstractJsonEncoderDecoder.STRING.decode(AbstractJsonEncoderDecoder.BOOLEAN.encode(b))); } public void testBooleanArrayDecode() { boolean[] array = { true, false }; AbstractJsonEncoderDecoder<Boolean> encoder = AbstractJsonEncoderDecoder.BOOLEAN; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new boolean[2]))); } public void testByteArrayDecode() { byte[] array = { 2, 8 }; AbstractJsonEncoderDecoder<Byte> encoder = AbstractJsonEncoderDecoder.BYTE; JSONValue json = AbstractJsonEncoderDecoder.toJSON(array, encoder); assertNotNull(json.isArray()); assertEquals(json.isArray().size(), 2); assertEquals(json.isArray().get(0).isNumber().doubleValue(), (double) array[0]); assertEquals(json.isArray().get(1).isNumber().doubleValue(), (double) array[1]); assertEquals("[2, 8]", Arrays.toString(AbstractJsonEncoderDecoder.toArray(json, encoder))); assertEquals(Arrays.toString(array), Arrays.toString( AbstractJsonEncoderDecoder.toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder))); Defaults.setByteArraysToBase64(true); try { json = AbstractJsonEncoderDecoder.toJSON(array, encoder); assertEquals("Agg=", json.isString().stringValue()); assertEquals("[2, 8]", Arrays.toString(AbstractJsonEncoderDecoder.toArray(json, encoder))); } finally { Defaults.setByteArraysToBase64(false); } } public void testCharacterArrayDecode() { char[] array = { 'a', 'z' }; AbstractJsonEncoderDecoder<Character> encoder = AbstractJsonEncoderDecoder.CHAR; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new char[2]))); } public void testFloatArrayDecode() { float[] array = { 1.4e19f, -13.53e-18f }; AbstractJsonEncoderDecoder<Float> encoder = AbstractJsonEncoderDecoder.FLOAT; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new float[2]))); } public void testDoubleArrayDecode() { double[] array = { 1.4e193, -13.53e-188 }; AbstractJsonEncoderDecoder<Double> encoder = AbstractJsonEncoderDecoder.DOUBLE; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new double[2]))); } public void testShortArrayDecode() { short[] array = { 1, -13 }; AbstractJsonEncoderDecoder<Short> encoder = AbstractJsonEncoderDecoder.SHORT; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new short[2]))); } public void testIntArrayDecode() { int[] array = { 1010, -13100 }; AbstractJsonEncoderDecoder<Integer> encoder = AbstractJsonEncoderDecoder.INT; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new int[2]))); } public void testLongArrayDecode() { long[] array = { 1010, -13100 }; AbstractJsonEncoderDecoder<Long> encoder = AbstractJsonEncoderDecoder.LONG; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new long[2]))); } public void testTypeArrayDecode() { String[] array = { "may", "all", "be", "happy" }; AbstractJsonEncoderDecoder<String> encoder = AbstractJsonEncoderDecoder.STRING; assertEquals(Arrays.toString(array), Arrays.toString(AbstractJsonEncoderDecoder .toArray(AbstractJsonEncoderDecoder.toJSON(array, encoder), encoder, new String[4]))); } public void testTypeMapDecode() { Map<String, Integer> map = new HashMap<String, Integer>(); map.put("123", 321); AbstractJsonEncoderDecoder<Integer> encoder = AbstractJsonEncoderDecoder.INT; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, encoder, Json.Style.DEFAULT), encoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, encoder, Json.Style.JETTISON_NATURAL), encoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithIntegerDecode() { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(123, "321"); AbstractJsonEncoderDecoder<Integer> keyEncoder = AbstractJsonEncoderDecoder.INT; AbstractJsonEncoderDecoder<String> valueEncoder = AbstractJsonEncoderDecoder.STRING; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithBigDecimalDecode() { Map<BigDecimal, String> map = new HashMap<BigDecimal, String>(); map.put(BigDecimal.valueOf(123), "321"); AbstractJsonEncoderDecoder<BigDecimal> keyEncoder = AbstractJsonEncoderDecoder.BIG_DECIMAL; AbstractJsonEncoderDecoder<String> valueEncoder = AbstractJsonEncoderDecoder.STRING; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithLargeDigitStringDecode() { Map<String, String> map = new HashMap<String, String>(); map.put("1234567890123456789", "321"); AbstractJsonEncoderDecoder<String> keyEncoder = AbstractJsonEncoderDecoder.STRING; AbstractJsonEncoderDecoder<String> valueEncoder = AbstractJsonEncoderDecoder.STRING; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithJsonStringDecode() { Map<String, String> map = new HashMap<String, String>(); map.put("[1,2, 3]", "value"); AbstractJsonEncoderDecoder<String> keyEncoder = AbstractJsonEncoderDecoder.STRING; AbstractJsonEncoderDecoder<String> valueEncoder = AbstractJsonEncoderDecoder.STRING; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } static class Email { String name, email; public String toString() { return name + "<" + email + ">"; } } static interface EmailCodec extends JsonEncoderDecoder<Email> { } public void testTypeMapWithComplexDecode() { Map<Email, String> map = new HashMap<Email, String>(); Email email = new Email(); email.email = "me@example.com"; email.name = "me"; map.put(email, "done"); AbstractJsonEncoderDecoder<Email> keyEncoder = GWT.create(EmailCodec.class); AbstractJsonEncoderDecoder<String> valueEncoder = AbstractJsonEncoderDecoder.STRING; assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithListValueDecode() { Map<String, List<String>> map = new HashMap<String, List<String>>(); map.put("key", new ArrayList<String>(Arrays.asList("me and the corner"))); AbstractJsonEncoderDecoder<List<String>> valueEncoder = AbstractNestedJsonEncoderDecoder .listEncoderDecoder(AbstractJsonEncoderDecoder.STRING); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, valueEncoder, Json.Style.DEFAULT), valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, valueEncoder, Json.Style.JETTISON_NATURAL), valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testCollectionValueDecode() { List<String> collection = new ArrayList<String>(Arrays.asList("me and the corner")); AbstractJsonEncoderDecoder<Collection<String>> valueEncoder = AbstractNestedJsonEncoderDecoder .collectionEncoderDecoder(AbstractJsonEncoderDecoder.STRING); assertEquals(collection.toString(), valueEncoder.decode(valueEncoder.encode(collection)).toString()); } public void testTypeMapWithMapValueDecode() { Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>(); Map<String, String> nested = new HashMap<String, String>(); nested.put("name", "me"); map.put("key", nested); AbstractJsonEncoderDecoder<Map<String, String>> valueEncoder = AbstractNestedJsonEncoderDecoder .mapEncoderDecoder(AbstractJsonEncoderDecoder.STRING, AbstractJsonEncoderDecoder.STRING, Json.Style.DEFAULT); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, valueEncoder, Json.Style.DEFAULT), valueEncoder, Json.Style.DEFAULT) .toString()); // the JETTISON enoding it is important to use the special encoder with String keys valueEncoder = AbstractNestedJsonEncoderDecoder.mapEncoderDecoder(AbstractJsonEncoderDecoder.STRING, Json.Style.JETTISON_NATURAL); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, valueEncoder, Json.Style.JETTISON_NATURAL), valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } public void testTypeMapWithListValueDecodeAndComplexKey() { Map<Email, List<String>> map = new HashMap<Email, List<String>>(); Email email = new Email(); email.email = "me@example.com"; email.name = "me"; map.put(email, new ArrayList<String>(Arrays.asList("me and the corner"))); AbstractJsonEncoderDecoder<Email> keyEncoder = GWT.create(EmailCodec.class); AbstractJsonEncoderDecoder<List<String>> valueEncoder = AbstractNestedJsonEncoderDecoder .listEncoderDecoder(AbstractJsonEncoderDecoder.STRING); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.DEFAULT), keyEncoder, valueEncoder, Json.Style.DEFAULT) .toString()); assertEquals(map.toString(), AbstractJsonEncoderDecoder .toMap(AbstractJsonEncoderDecoder.toJSON(map, keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL), keyEncoder, valueEncoder, Json.Style.JETTISON_NATURAL) .toString()); } static interface WithArraysAndCollectionsCodec extends JsonEncoderDecoder<WithArraysAndCollections> { } @SuppressWarnings("unchecked") public void testTypeWithArrasAndCollections() { WithArraysAndCollections obj = new WithArraysAndCollections(); obj.ages = new int[] { 1, 2, 3, 4 }; obj.ageSet = new HashSet<int[]>(); obj.ageSet.add(obj.ages); Email email = new Email(); email.email = "me@example.com"; email.name = "me"; obj.emailArray = new Email[] { email }; obj.emailList = new ArrayList<Email>(); obj.emailList.add(email); obj.emailSet = new HashSet<Email>(); obj.emailSet.add(email); obj.emailListArray = new List[1]; obj.emailListArray[0] = obj.emailList; obj.emailSetArray = new Set[1]; obj.emailSetArray[0] = obj.emailSet; obj.personalEmailList = new HashMap<String, List<Email>>(); obj.personalEmailList.put("me", obj.emailList); obj.personalEmailSet = new HashMap<String, Set<Email>>(); obj.personalEmailSet.put("me", obj.emailSet); obj.personalEmailListArray = new HashMap<String, List<Email>[]>(); obj.personalEmailListArray.put("me", obj.emailListArray); obj.personalEmailSetArray = new HashMap<String, Set<Email>[]>(); obj.personalEmailSetArray.put("me", obj.emailSetArray); obj.personalEmailSetList = new ArrayList<Map<String, Set<Email>>>(); obj.personalEmailSetList.add(obj.personalEmailSet); obj.personalEmailListSet = new HashSet<Map<String, List<Email>>>(); obj.personalEmailListSet.add(obj.personalEmailList); obj.personalEmailSetMap = new HashMap<Email, Map<String, Set<Email>>>(); obj.personalEmailSetMap.put(email, obj.personalEmailSet); AbstractJsonEncoderDecoder<WithArraysAndCollections> encoder = GWT .create(WithArraysAndCollectionsCodec.class); JSONValue json = encoder.encode(obj); assertEquals("{\"ages\":[1,2,3,4], " + "\"emailArray\":[{\"name\":\"me\", \"email\":\"me@example.com\"}], " + "\"emailList\":[{\"name\":\"me\", \"email\":\"me@example.com\"}], " + "\"emailSet\":[{\"name\":\"me\", \"email\":\"me@example.com\"}], " + "\"emailListArray\":[[{\"name\":\"me\", \"email\":\"me@example.com\"}]], " + "\"emailSetArray\":[[{\"name\":\"me\", \"email\":\"me@example.com\"}]], " + "\"personalEmailList\":{\"me\":[{\"name\":\"me\", \"email\":\"me@example.com\"}]}, " + "\"personalEmailSet\":{\"me\":[{\"name\":\"me\", \"email\":\"me@example.com\"}]}" + ", \"personalEmailListArray\":{\"me\":[[{\"name\":\"me\", \"email\":\"me@example.com\"}]]}" + ", \"personalEmailSetArray\":{\"me\":[[{\"name\":\"me\", \"email\":\"me@example.com\"}]]}" + ", \"personalEmailSetList\":[{\"me\":[{\"name\":\"me\", \"email\":\"me@example.com\"}]}]" + ", \"personalEmailListSet\":[{\"me\":[{\"name\":\"me\", \"email\":\"me@example.com\"}]}]" + ", \"personalEmailSetMap\":{\"{\\\"name\\\":\\\"me\\\", \\\"email\\\":\\\"me@example.com\\\"}\":" + "{\"me\":[{\"name\":\"me\", \"email\":\"me@example.com\"}]}}" + "}", json.toString()); WithArraysAndCollections roundtrip = encoder.decode(json); assertEquals("[1, 2, 3, 4],[me<me@example.com>],[me<me@example.com>],{me=[me<me@example.com>]},null," + "[me<me@example.com>],{me=[me<me@example.com>]},[[me<me@example.com>]],[[me<me@example.com>]]," + "[me]=>[[me<me@example.com>]],[me]=>[[me<me@example.com>]],[{me=[me<me@example.com>]}],[{me=[me<me@example.com>]}]," + "{me<me@example.com>={me=[me<me@example.com>]}}", roundtrip.toString()); } static class CCC { int age; @JsonIgnore String noAge; @XmlTransient int noAgeJax; @JsonIgnore private String lastName; String name; @JsonIgnore String firstName; transient public byte[] document; public void setDocument(String v) { document = v.getBytes(); } public String getDocument() { return new String(document); } String getLastName() { return lastName; } void setLastName(String name) { lastName = name; } @JsonIgnore String getAge() { return noAge; } void setAge(String age) { this.noAge = age; } } static interface CCCCodec extends JsonEncoderDecoder<CCC> { } public void testIgnores() { CCCCodec cccc = GWT.create(CCCCodec.class); CCC ccc = new CCC(); ccc.age = 20; ccc.noAgeJax = 12; ccc.name = "me and the corner"; ccc.firstName = "chaos"; ccc.lastName = "club"; ccc.setDocument("resty-docu"); JSONValue json = cccc.encode(ccc); assertEquals("{\"age\":20, \"name\":\"me and the corner\", \"document\":\"resty-docu\"}", json.toString()); CCC roundTrip = cccc.decode(json); assertEquals(ccc.name, roundTrip.name); assertEquals(ccc.age, roundTrip.age); assertNull(roundTrip.firstName); assertNull(roundTrip.getLastName()); assertNull(roundTrip.getAge()); } static class Shorty { private short shorty; private long id; public Shorty() { shorty = 0; } public short getShorty() { return shorty; } public void setShorty(short shorty) { this.shorty = shorty; } public long getId() { return id; } public void setId(long id) { this.id = id; } } static interface ShortyCodec extends JsonEncoderDecoder<Shorty> { } public void testShortys() { ShortyCodec shortyCodec = GWT.create(ShortyCodec.class); Shorty shorty = new Shorty(); JSONValue json = shortyCodec.encode(shorty); assertEquals("{\"shorty\":0, \"id\":0}", json.toString()); Shorty roundTrip = shortyCodec.decode(json); assertEquals(shorty.getShorty(), 0); assertEquals(roundTrip.getShorty(), 0); } public void testSuperlongLongs() { ShortyCodec shortyCodec = GWT.create(ShortyCodec.class); Shorty shorty = new Shorty(); shorty.id = 9007199254741115l;// = 2^53 + 123; // TODO that result is just wrong !!!! JSONValue json = shortyCodec.encode(shorty); assertEquals("{\"shorty\":0, \"id\":9007199254741116}", json.toString()); Shorty roundTrip = shortyCodec.decode(json); assertEquals(roundTrip.getId(), 9007199254741116l); } public void testSuperlongLongsAsString() { ShortyCodec shortyCodec = GWT.create(ShortyCodec.class); JSONObject json = new JSONObject(); json.put("shorty", new JSONNumber(0)); json.put("id", new JSONString("9007199254741115")); assertEquals("{\"shorty\":0, \"id\":\"9007199254741115\"}", json.toString()); Shorty roundTrip = shortyCodec.decode(json); assertEquals(roundTrip.getId(), 9007199254741115l); } static class Bean { @JsonIgnore private int myAge = 123; @XmlTransient private int myAgeJax = 123; int getAge() { return myAge; } void setAge(int a) { this.myAge = a; } void setYearOfBirth(long a) { } long getYearOfBirth() { return 1234; } } static class NestedBean extends Bean { String name = "asterix"; String getName() { return name; } void setName(String name) { this.name = name; } } static interface BeanCodec extends JsonEncoderDecoder<Bean> { } static interface NestedBeanCodec extends JsonEncoderDecoder<NestedBean> { } public void testBean() { BeanCodec beanCodec = GWT.create(BeanCodec.class); Bean bean = new Bean(); JSONValue json = beanCodec.encode(bean); // the order of keys depends on jdk7 vs. jdk8 so look at each key separately assertEquals(123.0, json.isObject().get("age").isNumber().doubleValue()); assertEquals(1234.0, json.isObject().get("yearOfBirth").isNumber().doubleValue()); Bean roundTrip = beanCodec.decode(json); assertEquals(roundTrip.getAge(), 123); assertEquals(roundTrip.getYearOfBirth(), 1234); } public void testNestedBean() { NestedBeanCodec beanCodec = GWT.create(NestedBeanCodec.class); NestedBean bean = new NestedBean(); JSONValue json = beanCodec.encode(bean); // the order of keys depends on jdk7 vs. jdk8 so look at each key separately assertEquals(123.0, json.isObject().get("age").isNumber().doubleValue()); assertEquals(1234.0, json.isObject().get("yearOfBirth").isNumber().doubleValue()); assertEquals("asterix", json.isObject().get("name").isString().stringValue()); NestedBean roundTrip = beanCodec.decode(json); assertEquals(roundTrip.getAge(), 123); assertEquals(roundTrip.getYearOfBirth(), 1234); } static class Renamed { @JsonProperty("my-age") private int age; @Json(name = "year-of-birth") private long yearOfBirth; private String n; private Integer vacationActivityIdForEmployee; private Integer vacationActivityIdForEmployer; @JsonProperty("vacationId") public Integer getVacationActivityIdForEmployee() { return vacationActivityIdForEmployee; } public void setVacationActivityIdForEmployee(Integer v) { vacationActivityIdForEmployee = v; } public Integer getVacationActivityIdForEmployer() { return vacationActivityIdForEmployer; } @JsonProperty("vacation-id") public void setVacationActivityIdForEmployer(Integer v) { vacationActivityIdForEmployer = v; } int getAge() { return age; } void setAge(int a) { this.age = a; } void setYearOfBirth(long a) { this.yearOfBirth = a; } long getYearOfBirth() { return yearOfBirth; } void setName(String name) { this.n = name; } @JsonProperty("my-name") String getName() { return n; } } static interface RenamedCodec extends JsonEncoderDecoder<Renamed> { } public void testRenamed() { RenamedCodec renamedCodec = GWT.create(RenamedCodec.class); Renamed renamed = new Renamed(); renamed.setName("marvin the robot"); renamed.setAge(123); renamed.setYearOfBirth(1234); renamed.setVacationActivityIdForEmployee(34); renamed.setVacationActivityIdForEmployer(42); JSONValue json = renamedCodec.encode(renamed); String[] values = json.toString().replace("}", "").replace("{", "").split(",\\s"); Arrays.sort(values); assertEquals( "[\"my-age\":123, \"my-name\":\"marvin the robot\", \"vacation-id\":42, \"vacationId\":34, \"year-of-birth\":1234]", Arrays.toString(values)); Renamed roundTrip = renamedCodec.decode(json); assertEquals(roundTrip.age, 123); assertEquals(roundTrip.yearOfBirth, 1234); assertEquals(roundTrip.getVacationActivityIdForEmployee().intValue(), 34); assertEquals(roundTrip.getName(), "marvin the robot"); } static public enum Language { ENGLISH("en"), FRENCH("fr"); private String name; @JsonCreator private Language(String name) { this.name = name; } @JsonValue public String getName() { return name; } } static public class LangRequest { public Language lang; } static interface LangRequestCodec extends JsonEncoderDecoder<LangRequest> { } public void testEnumAndJsonValue() { LangRequestCodec codec = GWT.create(LangRequestCodec.class); LangRequest pojo = new LangRequest(); pojo.lang = Language.FRENCH; JSONValue json = codec.encode(pojo); assertEquals("{\"lang\":\"fr\"}", json.toString()); LangRequest roundTrip = codec.decode(json); assertEquals(roundTrip.lang, Language.FRENCH); } static class WithEnum { enum Cycle { BEGIN, LIFE, END } public Cycle first; private Cycle last; public Cycle getLast() { return last; } public void setLast(Cycle last) { this.last = last; } } static interface WithEnumCodec extends JsonEncoderDecoder<WithEnum> { } public void testWithEnum() { WithEnumCodec codec = GWT.create(WithEnumCodec.class); WithEnum pojo = new WithEnum(); pojo.first = WithEnum.Cycle.BEGIN; pojo.setLast(WithEnum.Cycle.END); JSONValue json = codec.encode(pojo); assertEquals("{\"first\":\"BEGIN\", \"last\":\"END\"}", json.toString()); WithEnum roundTrip = codec.decode(json); assertEquals(roundTrip.first, Cycle.BEGIN); assertEquals(roundTrip.getLast(), Cycle.END); pojo.first = null; pojo.setLast(null); json = codec.encode(pojo); assertEquals("{\"first\":null, \"last\":null}", json.toString()); roundTrip = codec.decode(json); assertEquals(roundTrip.first, null); assertEquals(roundTrip.getLast(), null); } static class WithOptionalPrimitive { public Optional<Integer> bar; } static interface WithOptionalPrimitiveCodec extends JsonEncoderDecoder<WithOptionalPrimitive> { } public void testCustomWithOptionalPrimitive() { WithOptionalPrimitiveCodec codec = GWT.create(WithOptionalPrimitiveCodec.class); WithOptionalPrimitive pojo = new WithOptionalPrimitive(); pojo.bar = Optional.absent(); JSONValue json = codec.encode(pojo); assertEquals("{}", json.toString()); WithOptionalPrimitive roundTrip = codec.decode(json); assertEquals(roundTrip.bar, Optional.<Integer>absent()); pojo.bar = Optional.of(1); json = codec.encode(pojo); assertEquals("{\"bar\":1}", json.toString()); roundTrip = codec.decode(json); assertEquals(roundTrip.bar, Optional.of(1)); } static class WithOptionalPojo { public Optional<Name> name; } static interface WithOptionalPojoCodec extends JsonEncoderDecoder<WithOptionalPojo> { } public void testCustomWithOptionalPojo() { WithOptionalPojoCodec codec = GWT.create(WithOptionalPojoCodec.class); WithOptionalPojo pojo = new WithOptionalPojo(); pojo.name = Optional.absent(); JSONValue json = codec.encode(pojo); assertEquals("{}", json.toString()); WithOptionalPojo roundTrip = codec.decode(json); assertEquals(roundTrip.name, Optional.<Name>absent()); Name n = new Name(); n.name = "name1"; pojo.name = Optional.of(n); json = codec.encode(pojo); assertEquals("{\"name\":{\"name\":\"name1\"}}", json.toString()); roundTrip = codec.decode(json); assertEquals(roundTrip.name.get().name, n.name); } /** * Classes for SubClassHierarchyEncoderDecoder */ static class Base { } static class ExtBase extends Base { } static class OneMoreExtBase extends ExtBase { } static class Other extends Base { } static interface SubClassHierarchyRestService extends RestService { @GET @Path("/") void myendpoint(MethodCallback<OneMoreExtBase> callback); } public void testSubClassHierarchyEncoderDecoder() { SubClassHierarchyRestService service = GWT.create(SubClassHierarchyRestService.class); // we won't even get here if "Other" is put in the generated encoder/decoder for OneMoreExtBase assertNotNull(service); } static class MoreSpecificFieldThanConstructor { private final TreeMap<String, String> elements; @JsonCreator MoreSpecificFieldThanConstructor(@JsonProperty("elements") final Map<String, String> elements) { this.elements = new TreeMap<String, String>(elements); } public Map<String, String> getElements() { return elements; } } static interface MoreSpecificFieldThanConstructorCodec extends JsonEncoderDecoder<MoreSpecificFieldThanConstructor> { } public void testMapFieldAsConstructorParam() { MoreSpecificFieldThanConstructorCodec codec = GWT.create(MoreSpecificFieldThanConstructorCodec.class); JSONValue value = codec.encode( new MoreSpecificFieldThanConstructor(Collections.<String, String>singletonMap("foo", "bar"))); assertEquals(value.toString(), codec.encode(codec.decode(value)).toString()); } @JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "@class") @JsonSubTypes({ @Type(DefaultImplementationOfSubTypeInterface.class), @Type(SecondImplementationOfSubTypeInterface.class) }) interface JsonSubTypesWithAnInterface { String getValue(); } static abstract class AbstractSubType implements JsonSubTypesWithAnInterface { } static class DefaultImplementationOfSubTypeInterface extends AbstractSubType { private String value; @JsonCreator public DefaultImplementationOfSubTypeInterface(@JsonProperty("value") String value) { this.value = value; } @Override public String getValue() { return value; } } static class SecondImplementationOfSubTypeInterface extends AbstractSubType { public String value; @Override public String getValue() { return value; } } static interface JsonSubTypesWithAnInterfaceCodec extends JsonEncoderDecoder<JsonSubTypesWithAnInterface> { } static interface JsonSubTypesWithAnInterfaceImplementationCodec extends JsonEncoderDecoder<DefaultImplementationOfSubTypeInterface> { } public void testJsonSubTypesWithAnInterface() { JsonSubTypesWithAnInterfaceCodec codec = GWT.create(JsonSubTypesWithAnInterfaceCodec.class); String value = "Hello, world!"; JsonSubTypesWithAnInterface o1 = new DefaultImplementationOfSubTypeInterface(value); JSONValue json = codec.encode(o1); assertEquals(json.isObject().get("@class").isString().stringValue(), DefaultImplementationOfSubTypeInterface.class.getName().replace("$", ".")); JsonSubTypesWithAnInterface o2 = codec.decode(json); assertEquals(json.toString(), codec.encode(o2).toString()); assertEquals(value, o1.getValue()); assertEquals(o1.getValue(), o2.getValue()); assertEquals(o2.getClass(), DefaultImplementationOfSubTypeInterface.class); } public void testJsonSubTypesWithInterfaceUsingConcreteImplementationCodec() { JsonSubTypesWithAnInterfaceImplementationCodec codec = GWT .create(JsonSubTypesWithAnInterfaceImplementationCodec.class); String value = "Hello, world!"; DefaultImplementationOfSubTypeInterface o1 = new DefaultImplementationOfSubTypeInterface(value); JSONValue json = codec.encode(o1); JSONValue objectClass = json.isObject().get("@class"); assertNotNull(objectClass); assertEquals(objectClass.isString().stringValue(), DefaultImplementationOfSubTypeInterface.class.getName().replace("$", ".")); DefaultImplementationOfSubTypeInterface o2 = codec.decode(json); assertEquals(json.toString(), codec.encode(o2).toString()); assertEquals(value, o1.getValue()); } @JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "@class") @JsonSubTypes({ @Type(EnumOfSubTypeInterface.class) }) interface JsonSubTypesWithAnInterfaceForUseWithAnEnum { @JsonProperty("name") String name(); } enum EnumOfSubTypeInterface implements JsonSubTypesWithAnInterfaceForUseWithAnEnum { HELLO, WORLD } static interface JsonSubTypesWithAnInterfaceForUseWithAnEnumCodec extends JsonEncoderDecoder<JsonSubTypesWithAnInterfaceForUseWithAnEnum> { } public void testJsonSubTypesWithAnInterfaceImplementedByAnEnum() { JsonSubTypesWithAnInterfaceForUseWithAnEnumCodec codec = GWT .create(JsonSubTypesWithAnInterfaceForUseWithAnEnumCodec.class); JSONValue json = codec.encode(EnumOfSubTypeInterface.HELLO); JsonSubTypesWithAnInterfaceForUseWithAnEnum useWithAnEnum = codec.decode(json); assertEquals(useWithAnEnum.name(), EnumOfSubTypeInterface.HELLO.name()); } }