From fe5e4f03904e5067a17b72857f5287a68c22dec1 Mon Sep 17 00:00:00 2001 From: Dominik Smogor Date: Tue, 22 Aug 2017 10:50:40 +0200 Subject: [PATCH] added class json attribute support in number of cases, notably abstract classes within Map and List --- .../org/boon/core/reflection/Annotations.java | 2 +- .../boon/core/reflection/MapperComplex.java | 24 +++-- .../boon/core/reflection/MapperSimple.java | 59 +++++++++---- .../java/org/boon/json/code/InterfaceB.java | 13 +++ .../json/code/JsonAbstractClassesTest.java | 88 +++++++++++++++++++ .../java/org/boon/json/code/JsonClassA.java | 42 +++++++++ .../java/org/boon/json/code/JsonClassC.java | 22 +++++ .../java/org/boon/json/code/JsonClassD.java | 19 ++++ 8 files changed, 247 insertions(+), 22 deletions(-) create mode 100644 boon/src/test/java/org/boon/json/code/InterfaceB.java create mode 100644 boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java create mode 100644 boon/src/test/java/org/boon/json/code/JsonClassA.java create mode 100644 boon/src/test/java/org/boon/json/code/JsonClassC.java create mode 100644 boon/src/test/java/org/boon/json/code/JsonClassD.java diff --git a/boon/src/main/java/org/boon/core/reflection/Annotations.java b/boon/src/main/java/org/boon/core/reflection/Annotations.java index ff182c03..bec0b776 100644 --- a/boon/src/main/java/org/boon/core/reflection/Annotations.java +++ b/boon/src/main/java/org/boon/core/reflection/Annotations.java @@ -300,7 +300,7 @@ private static Annotation[] extractAllAnnotationsForProperty( Class clazz, St /* In the land of dynamic proxied AOP classes, * this class could be a proxy. This seems like a bug * waiting to happen. So far it has worked... */ - if ( annotations.length == 0 ) { + if ( annotations.length == 0 && clazz.getSuperclass() != null) { annotations = findPropertyAnnotations( clazz.getSuperclass(), propertyName, useRead ); } return annotations; diff --git a/boon/src/main/java/org/boon/core/reflection/MapperComplex.java b/boon/src/main/java/org/boon/core/reflection/MapperComplex.java index 6b7bc42f..f282ef82 100644 --- a/boon/src/main/java/org/boon/core/reflection/MapperComplex.java +++ b/boon/src/main/java/org/boon/core/reflection/MapperComplex.java @@ -180,14 +180,15 @@ public List convertListOfMapsToObjects(List list, Class componen /** * fromMap converts a map into a java object * @param map map to create the object from. - * @param cls class type of new object + * @param c class type of new object * @param map to create teh object from. * @return new object of type cls */ @Override - public T fromMap(final Map map, final Class cls) { + public T fromMap(final Map map, final Class c) { + final Class cls = MapperSimple.handleAbstractReplacement(map, c); T toObject = Reflection.newInstance( cls ); Map fields = fieldsAccessor.getFields( toObject.getClass() ); Set> mapKeyValuesEntrySet = map.entrySet(); @@ -1171,14 +1172,15 @@ public Object fromValueMap(final Map valueMap * This does some special handling to take advantage of us using the value map so it avoids creating * a bunch of array objects and collections. Things you have to worry about when writing a * high-speed JSON serializer. - * @param cls the new type + * @param c the new type * @return new object from value map */ @Override @SuppressWarnings("unchecked") public T fromValueMap(final Map valueMap, - final Class cls) { + final Class c) { + final Class cls = MapperSimple.handleAbstractReplacement(valueMap, c); T newInstance = Reflection.newInstance( cls ); ValueMap map = ( ValueMap ) ( Map ) valueMap; @@ -1372,8 +1374,18 @@ private void fromValueMapHandleValueCase( evalue = ((ValueContainer) evalue).toValue(); } + + key = Conversions.coerce( keyType, key ); - evalue = Conversions.coerce( valueType, evalue ); + + + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } @@ -1842,4 +1854,4 @@ public List toList(Object object) { } -} \ No newline at end of file +} diff --git a/boon/src/main/java/org/boon/core/reflection/MapperSimple.java b/boon/src/main/java/org/boon/core/reflection/MapperSimple.java index 87fe1eab..80a64332 100644 --- a/boon/src/main/java/org/boon/core/reflection/MapperSimple.java +++ b/boon/src/main/java/org/boon/core/reflection/MapperSimple.java @@ -79,19 +79,36 @@ public List convertListOfMapsToObjects(List list, Class componen return ( List ) newList; } + static Class handleAbstractReplacement(final Map map, final Class cls) { + if (cls.isInterface() && Typ.isAbstract(cls)) { + if(map.containsKey("class")) { + String className = map.get("class").toString(); + if (className != null) { + Class c = Reflection.loadClass(className); + if (cls.isAssignableFrom(c)) { + return c; + } + } + } + } + return cls; + + } - /** - * fromMap converts a map into a java object - * @param map map to create the object from. - * @param cls class type of new object - * @param map to create teh object from. - * @return new object of type cls - */ - @Override - public T fromMap(final Map map, final Class cls) { + + + /** + * fromMap converts a map into a java object + * @param map map to create the object from. + * @param map to create teh object from. + * @return new object of type cls + */ + @Override + public T fromMap(final Map map, final Class c) { + final Class cls = handleAbstractReplacement(map, c); T toObject = Reflection.newInstance( cls ); Map fields = fieldsAccessor.getFields( toObject.getClass() ); Set> mapKeyValuesEntrySet = map.entrySet(); @@ -1042,8 +1059,7 @@ private void handleCollectionOfValues( */ @Override @SuppressWarnings("unchecked") - public Object fromValueMap(final Map valueMap - ) { + public Object fromValueMap(final Map valueMap) { try { @@ -1063,14 +1079,15 @@ public Object fromValueMap(final Map valueMap * This does some special handling to take advantage of us using the value map so it avoids creating * a bunch of array objects and collections. Things you have to worry about when writing a * high-speed JSON serializer. - * @param cls the new type + * @param c the new type * @return new object from value map */ @Override @SuppressWarnings("unchecked") public T fromValueMap(final Map valueMap, - final Class cls) { + final Class c) { + final Class cls = handleAbstractReplacement(valueMap, c); T newInstance = Reflection.newInstance( cls ); ValueMap map = ( ValueMap ) ( Map ) valueMap; @@ -1259,7 +1276,13 @@ private void fromValueMapHandleValueCase( } key = Conversions.coerce( keyType, key ); - evalue = Conversions.coerce( valueType, evalue ); + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } @@ -1352,7 +1375,13 @@ private void setFieldValueFromMap( final Object parentObject, } key = Conversions.coerce(keyType, key); - evalue = Conversions.coerce( valueType, evalue ); + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } diff --git a/boon/src/test/java/org/boon/json/code/InterfaceB.java b/boon/src/test/java/org/boon/json/code/InterfaceB.java new file mode 100644 index 00000000..9d76faee --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/InterfaceB.java @@ -0,0 +1,13 @@ +package org.boon.json.code; + + +/** + * Created by piyush.goyal on 9/22/16. + */ +public interface InterfaceB { + + String getPiyush(); + + void setPiyush(String piyush); + +} diff --git a/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java b/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java new file mode 100644 index 00000000..103d16f6 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java @@ -0,0 +1,88 @@ +package org.boon.json.code; + +import org.boon.json.JsonFactory; +import org.boon.json.JsonParserFactory; +import org.boon.json.JsonSerializer; +import org.boon.json.JsonSerializerFactory; +import org.boon.json.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by piyush.goyal on 8/17/17. + */ +public class JsonAbstractClassesTest { + + + + private ObjectMapper mapper; + + @Test + public void testAbstracTClassesWSimpleMapper() { + mapper = JsonFactory.create(new JsonParserFactory().useAnnotations(), + new JsonSerializerFactory().useAnnotations()); + testAbstracTClasses(); + } + @Test + public void testAbstracTClassesWComplexMapper() { + mapper = JsonFactory.create(new JsonParserFactory().setRespectIgnore(false).useAnnotations(), + new JsonSerializerFactory().useAnnotations()); + testAbstracTClasses(); + } + + public void testAbstracTClasses() { + + + JsonClassC c = new JsonClassC(); + c.setPiyush("Tosheer"); + + JsonClassD d = new JsonClassD(); + d.setPiyush("VIvek"); + + Map map = new HashMap<>(); + map.put("c", c); + map.put("d", d); + + List list = new ArrayList<>(); + list.add(c); + list.add(d); + + JsonClassA jsonClassA = new JsonClassA(); + + jsonClassA.setTextValue("aa"); + jsonClassA.setXx(map); + jsonClassA.setYy(list); + + JsonSerializer serializer = new JsonSerializerFactory().setOutputType(true).create(); + + String s = serializer.serialize(jsonClassA).toString(); + System.out.println(s); + + //String json = "{\"class\":\"com.akqa.some.code.JsonClassA\",\"xx\":{\"c\":{\"class\":\"com.akqa.some.code.JsonClassC\",\"piyush\":\"Tosheer\"},\"d\":{\"class\":\"com.akqa.some.code.JsonClassD\",\"piyush\":\"VIvek\"}},\"textValue\":\"aa\"}"; + String json = s; + JsonClassA jsonClassA1 = mapper.fromJson(json, JsonClassA.class); + + + assertEquals("aa", jsonClassA1.getTextValue()); + Map xx = jsonClassA1.getXx(); + InterfaceB c1 = xx.get("c"); + InterfaceB d1 = xx.get("d"); + assertEquals("Tosheer", c1.getPiyush()); + assertEquals("VIvek", d1.getPiyush()); + assertEquals(c.getClass(), c1.getClass()); + assertEquals(d.getClass(), d1.getClass()); + List list1= jsonClassA1.getYy(); + c1 = list1.get(0); + d1 = list1.get(1); + assertEquals("Tosheer", c1.getPiyush()); + assertEquals("VIvek", d1.getPiyush()); + assertEquals(c.getClass(), c1.getClass()); + assertEquals(d.getClass(), d1.getClass()); + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassA.java b/boon/src/test/java/org/boon/json/code/JsonClassA.java new file mode 100644 index 00000000..034d44c3 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassA.java @@ -0,0 +1,42 @@ +package org.boon.json.code; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassA { + + + private Map xx = new HashMap<>(); + + private List yy = new ArrayList<>(); + private String textValue; + + public String getTextValue() { + return textValue; + } + + public void setTextValue(String textValue) { + this.textValue = textValue; + } + + public Map getXx() { + return xx; + } + + public void setXx(Map xx) { + this.xx = xx; + } + + public List getYy() { + return yy; + } + + public void setYy(List yy) { + this.yy = yy; + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassC.java b/boon/src/test/java/org/boon/json/code/JsonClassC.java new file mode 100644 index 00000000..02bf42dd --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassC.java @@ -0,0 +1,22 @@ +package org.boon.json.code; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassC implements InterfaceB { + + protected JsonClassC(){} + + + private String piyush; + + @Override + public String getPiyush() { + return piyush; + } + + @Override + public void setPiyush(String piyush) { + this.piyush = piyush; + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassD.java b/boon/src/test/java/org/boon/json/code/JsonClassD.java new file mode 100644 index 00000000..ff102ad8 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassD.java @@ -0,0 +1,19 @@ +package org.boon.json.code; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassD implements InterfaceB { + + private String piyush; + + @Override + public String getPiyush() { + return piyush; + } + + @Override + public void setPiyush(String piyush) { + this.piyush = piyush; + } +}