/** * Copyright (C) 2009-2015 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.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.JSONParser; 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()); } /** * Jersey encode arrays with one object as object and not as array. */ @SuppressWarnings("unchecked") public void testArrayAndCollectionWithOneElementAsObject() { JSONValue json = JSONParser.parseStrict("{\"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\"}]}}" + "}"); AbstractJsonEncoderDecoder<WithArraysAndCollections> encoder = GWT.create(WithArraysAndCollectionsCodec.class); WithArraysAndCollections result = 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>]}}", result.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()); } public class BeanNamingConvention { private Integer xValue; private Integer myValue; public Integer getxValue() { return xValue; } public void setxValue(Integer xValue) { this.xValue = xValue; } public Integer getMyValue() { return myValue; } public void setMyValue(Integer myValue) { this.myValue = myValue; } } static interface BeanNamingConventionCodec extends JsonEncoderDecoder<BeanNamingConvention> { } public void testBeanNamingConvention() { BeanNamingConventionCodec codec = GWT.create(BeanNamingConventionCodec.class); BeanNamingConvention bean = new BeanNamingConvention(); bean.setxValue(1); bean.setMyValue(3); JSONValue json = codec.encode(bean); assertEquals("{\"xValue\":1, \"myValue\":3}", json.toString()); } static class ClassWithSubclass { private String name = "Max"; String getName() { return name; } void setName(String a) { this.name = a; } } static class SubclassWithoutEncoderDecoder extends ClassWithSubclass { String getName() { return super.getName() + " --> subclass"; } } static interface ClassWithSubclassCodec extends JsonEncoderDecoder<ClassWithSubclass> { } public void testSubclassCanBeEncodedBySuperclassEncoder() { ClassWithSubclassCodec codec = GWT.create(ClassWithSubclassCodec.class); SubclassWithoutEncoderDecoder subclassWithoutEncoderDecoder = new SubclassWithoutEncoderDecoder(); JSONValue json = codec.encode(subclassWithoutEncoderDecoder); assertEquals("Max --> subclass", json.isObject().get("name").isString().stringValue()); ClassWithSubclass roundTrip = codec.decode(json); assertEquals("Max --> subclass", roundTrip.getName()); } }