Repository: zest-qi4j Updated Branches: refs/heads/ZEST-22_toEntity-toValue 591562568 -> e6d2d6b59
Fix for ZEST-23. Project: http://git-wip-us.apache.org/repos/asf/zest-qi4j/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-qi4j/commit/e6d2d6b5 Tree: http://git-wip-us.apache.org/repos/asf/zest-qi4j/tree/e6d2d6b5 Diff: http://git-wip-us.apache.org/repos/asf/zest-qi4j/diff/e6d2d6b5 Branch: refs/heads/ZEST-22_toEntity-toValue Commit: e6d2d6b59a60fe93597df338d0ad29ad22caadbe Parents: 5915625 Author: Niclas Hedhman <[email protected]> Authored: Thu May 21 22:31:22 2015 +0800 Committer: Niclas Hedhman <[email protected]> Committed: Thu May 21 22:31:22 2015 +0800 ---------------------------------------------------------------------- .../main/java/org/qi4j/api/type/MapType.java | 27 ++++++++++++++++ .../entity/associations/AssociationTest.java | 1 + .../spi/value/ValueDeserializerAdapter.java | 34 +++++++++++++++++--- .../qi4j/spi/value/ValueSerializerAdapter.java | 4 +-- .../orgjson/OrgJsonValueDeserializer.java | 29 +++++++++++++++++ .../AbstractCollectionSerializationTest.java | 1 + .../jackson/JacksonValueDeserializer.java | 26 +++++++++++++++ .../stax/StaxValueDeserializer.java | 23 +++++++++++++ 8 files changed, 138 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/api/src/main/java/org/qi4j/api/type/MapType.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/type/MapType.java b/core/api/src/main/java/org/qi4j/api/type/MapType.java index ddf03c5..753500a 100644 --- a/core/api/src/main/java/org/qi4j/api/type/MapType.java +++ b/core/api/src/main/java/org/qi4j/api/type/MapType.java @@ -27,6 +27,17 @@ public final class MapType private ValueType keyType; private ValueType valueType; + private final Variant variant; + + /** Two Variants are made distinct. + * <p> + * The {@code entry} type represents the explicit key=keyValue, value=valueValue. + * </p> + * <p> + * The {@code object} type represents the explicit keyValue=valueValue. + * </p> + */ + public enum Variant { entry, object } public static boolean isMap( Type type ) { @@ -39,11 +50,22 @@ public final class MapType return new MapType( Map.class, ValueType.of( keyType ), ValueType.of( valueType ) ); } + public static MapType of( Class<?> keyType, Class<?> valueType, Variant variant ) + { + return new MapType( Map.class, ValueType.of( keyType ), ValueType.of( valueType ), variant ); + } + public MapType( Class<?> type, ValueType keyType, ValueType valueType ) { + this( type, keyType, valueType, Variant.entry ); + } + + public MapType( Class<?> type, ValueType keyType, ValueType valueType, Variant variant ) + { super( type ); this.keyType = keyType; this.valueType = valueType; + this.variant = variant; if( !isMap( type ) ) { throw new IllegalArgumentException( type + " is not a Map." ); @@ -60,6 +82,11 @@ public final class MapType return valueType; } + public Variant variant() + { + return variant; + } + @Override public String toString() { http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/runtime/src/test/java/org/qi4j/runtime/entity/associations/AssociationTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/qi4j/runtime/entity/associations/AssociationTest.java b/core/runtime/src/test/java/org/qi4j/runtime/entity/associations/AssociationTest.java index 7772245..ac321c9 100644 --- a/core/runtime/src/test/java/org/qi4j/runtime/entity/associations/AssociationTest.java +++ b/core/runtime/src/test/java/org/qi4j/runtime/entity/associations/AssociationTest.java @@ -101,6 +101,7 @@ public class AssociationTest public interface Friend<T> { + @Optional Association<T> friend(); } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/spi/src/main/java/org/qi4j/spi/value/ValueDeserializerAdapter.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/qi4j/spi/value/ValueDeserializerAdapter.java b/core/spi/src/main/java/org/qi4j/spi/value/ValueDeserializerAdapter.java index 6663150..b20bd79 100644 --- a/core/spi/src/main/java/org/qi4j/spi/value/ValueDeserializerAdapter.java +++ b/core/spi/src/main/java/org/qi4j/spi/value/ValueDeserializerAdapter.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; -import java.util.logging.Logger; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; @@ -687,7 +686,7 @@ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> Object value = getObjectFieldValue( inputNode, namedAssociationName, - buildDeserializeInputNodeFunction( MapType.of( String.class, EntityReference.class ) ) ); + buildDeserializeInputNodeFunction( MapType.of( String.class, EntityReference.class, MapType.Variant.object ) ) ); stateMap.put( namedAssociationName, value ); } } @@ -755,7 +754,15 @@ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> else // Explicit Map if( MapType.class.isAssignableFrom( valueType.getClass() ) ) { - return (T) deserializeNodeMap( (MapType) valueType, inputNode ); + MapType mapType = (MapType) valueType; + if( mapType.variant().equals( MapType.Variant.entry ) ) + { + return (T) deserializeNodeEntryMap( (MapType) valueType, inputNode ); + } + else + { + return (T) deserializeNodeObjectMap( (MapType) valueType, inputNode ); + } } else // Enum if( EnumType.class.isAssignableFrom( valueType.getClass() ) || type.isEnum() ) @@ -857,7 +864,7 @@ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> return collection; } - private <K, V> Map<K, V> deserializeNodeMap( MapType mapType, InputNodeType inputNode ) + private <K, V> Map<K, V> deserializeNodeEntryMap( MapType mapType, InputNodeType inputNode ) throws Exception { Map<K, V> map = new HashMap<>(); @@ -868,6 +875,16 @@ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> return map; } + private <V> Map<String, V> deserializeNodeObjectMap( MapType mapType, InputNodeType inputNode ) + throws Exception + { + Map<String, V> map = new HashMap<>(); + putObjectNodeInMap( inputNode, + this.<V>buildDeserializeInputNodeFunction( mapType.valueType() ), + map ); + return map; + } + @SuppressWarnings( "unchecked" ) private <T> T deserializeNodeGuessed( ValueType valueType, InputNodeType inputNode ) throws Exception @@ -1070,6 +1087,13 @@ public abstract class ValueDeserializerAdapter<InputType, InputNodeType> protected abstract <K, V> void putArrayNodeInMap( InputNodeType inputNode, Function<InputNodeType, K> keyDeserializer, Function<InputNodeType, V> valueDeserializer, - Map<K, V> map ) + Map<K, V> map + ) + throws Exception; + + protected abstract <V> void putObjectNodeInMap( InputNodeType inputNode, + Function<InputNodeType, V> valueDeserializer, + Map<String, V> map + ) throws Exception; } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/spi/src/main/java/org/qi4j/spi/value/ValueSerializerAdapter.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/qi4j/spi/value/ValueSerializerAdapter.java b/core/spi/src/main/java/org/qi4j/spi/value/ValueSerializerAdapter.java index c392214..8b6246f 100644 --- a/core/spi/src/main/java/org/qi4j/spi/value/ValueSerializerAdapter.java +++ b/core/spi/src/main/java/org/qi4j/spi/value/ValueSerializerAdapter.java @@ -473,7 +473,7 @@ public abstract class ValueSerializerAdapter<OutputType> NamedAssociation<?> namedAssociation = state.namedAssociationFor( associationDescriptor.accessor() ); onFieldStart( output, associationDescriptor.qualifiedName().name() ); onValueStart( output ); - onArrayStart( output ); + onObjectStart( output ); for( String name : namedAssociation ) { onFieldStart( output, name ); @@ -482,7 +482,7 @@ public abstract class ValueSerializerAdapter<OutputType> onValueEnd( output ); onFieldEnd( output ); } - onArrayEnd( output ); + onObjectEnd( output ); onValueEnd( output ); onFieldEnd( output ); } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/spi/src/main/java/org/qi4j/valueserialization/orgjson/OrgJsonValueDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/qi4j/valueserialization/orgjson/OrgJsonValueDeserializer.java b/core/spi/src/main/java/org/qi4j/valueserialization/orgjson/OrgJsonValueDeserializer.java index 452f75e..223bc99 100644 --- a/core/spi/src/main/java/org/qi4j/valueserialization/orgjson/OrgJsonValueDeserializer.java +++ b/core/spi/src/main/java/org/qi4j/valueserialization/orgjson/OrgJsonValueDeserializer.java @@ -20,6 +20,7 @@ package org.qi4j.valueserialization.orgjson; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collection; +import java.util.Iterator; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; @@ -449,4 +450,32 @@ public class OrgJsonValueDeserializer } } } + + @Override + protected <V> void putObjectNodeInMap( Object inputNode, Function<Object, V> valueDeserializer, Map<String, V> map ) + throws Exception + { + if( JSONObject.NULL.equals( inputNode ) ) + { + return; + } + if( !( inputNode instanceof JSONObject ) ) + { + throw new ValueSerializationException( "Expected an object but got " + inputNode ); + } + JSONObject object = (JSONObject) inputNode; + + @SuppressWarnings( "unchecked" ) + Iterator<String> it = object.keys(); + while( it.hasNext() ) + { + String key = it.next(); + Object item = object.get( key ); + V valueValue = valueDeserializer.map( item ); + if( key != null ) + { + map.put( key, valueValue ); + } + } + } } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/core/testsupport/src/main/java/org/qi4j/test/value/AbstractCollectionSerializationTest.java ---------------------------------------------------------------------- diff --git a/core/testsupport/src/main/java/org/qi4j/test/value/AbstractCollectionSerializationTest.java b/core/testsupport/src/main/java/org/qi4j/test/value/AbstractCollectionSerializationTest.java index 5c58c2a..3a04ee5 100644 --- a/core/testsupport/src/main/java/org/qi4j/test/value/AbstractCollectionSerializationTest.java +++ b/core/testsupport/src/main/java/org/qi4j/test/value/AbstractCollectionSerializationTest.java @@ -69,6 +69,7 @@ public class AbstractCollectionSerializationTest { module.injectTo( this ); } + @Service @SuppressWarnings( "ProtectedField" ) protected ValueSerialization valueSerialization; http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/extensions/valueserialization-jackson/src/main/java/org/qi4j/valueserialization/jackson/JacksonValueDeserializer.java ---------------------------------------------------------------------- diff --git a/extensions/valueserialization-jackson/src/main/java/org/qi4j/valueserialization/jackson/JacksonValueDeserializer.java b/extensions/valueserialization-jackson/src/main/java/org/qi4j/valueserialization/jackson/JacksonValueDeserializer.java index 5c6774b..3d82f43 100644 --- a/extensions/valueserialization-jackson/src/main/java/org/qi4j/valueserialization/jackson/JacksonValueDeserializer.java +++ b/extensions/valueserialization-jackson/src/main/java/org/qi4j/valueserialization/jackson/JacksonValueDeserializer.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.InputStream; import java.util.Collection; +import java.util.Iterator; import java.util.Map; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; @@ -341,6 +342,31 @@ public class JacksonValueDeserializer } } + @Override + protected <V> void putObjectNodeInMap( JsonNode inputNode, + Function<JsonNode, V> valueDeserializer, + Map<String, V> map + ) + throws Exception + { + if( isNullOrMissing( inputNode ) ) + { + return; + } + if( !inputNode.isObject() ) + { + throw new ValueSerializationException( "Expected an object but got " + inputNode ); + } + ObjectNode object = (ObjectNode) inputNode; + Iterator<Map.Entry<String, JsonNode>> fields = object.fields(); + while( fields.hasNext() ) + { + Map.Entry<String, JsonNode> entry = fields.next(); + V value = valueDeserializer.map( entry.getValue() ); + map.put( entry.getKey(), value ); + } + } + private static boolean isNullOrMissing( JsonNode inputNode ) { return inputNode == null || inputNode.isNull() || inputNode.isMissingNode(); http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/e6d2d6b5/extensions/valueserialization-stax/src/main/java/org/qi4j/valueserialization/stax/StaxValueDeserializer.java ---------------------------------------------------------------------- diff --git a/extensions/valueserialization-stax/src/main/java/org/qi4j/valueserialization/stax/StaxValueDeserializer.java b/extensions/valueserialization-stax/src/main/java/org/qi4j/valueserialization/stax/StaxValueDeserializer.java index 9bdc34a..f162a62 100644 --- a/extensions/valueserialization-stax/src/main/java/org/qi4j/valueserialization/stax/StaxValueDeserializer.java +++ b/extensions/valueserialization-stax/src/main/java/org/qi4j/valueserialization/stax/StaxValueDeserializer.java @@ -404,6 +404,29 @@ public class StaxValueDeserializer } } + @Override + protected <V> void putObjectNodeInMap( Node inputNode, + Function<Node, V> valueDeserializer, + Map<String, V> map ) + throws Exception + { + if( inputNode == null ) + { + return; + } + NodeList entriesNodes = inputNode.getChildNodes(); + for( int idx = 0; idx < entriesNodes.getLength(); idx++ ) + { + Node entryNode = entriesNodes.item( idx ); + String key = ((Element) entryNode).getTagName(); + V value = getObjectFieldValue( entryNode, "value", valueDeserializer ); + if( key != null ) + { + map.put( key, value ); + } + } + } + @SuppressWarnings( "AssignmentToMethodParameter" ) private Object detectAndConvertStringValue( String stringValue ) {
