| /* |
| * Copyright (c) 2012, Paul Merlin. All Rights Reserved. |
| * |
| * 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.qi4j.valueserialization.jackson; |
| |
| import com.fasterxml.jackson.core.JsonFactory; |
| import com.fasterxml.jackson.core.JsonParser; |
| import com.fasterxml.jackson.core.JsonToken; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.MappingJsonFactory; |
| 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; |
| import org.qi4j.api.service.ServiceReference; |
| import org.qi4j.api.structure.Application; |
| import org.qi4j.api.structure.Module; |
| import org.qi4j.api.type.ValueType; |
| import org.qi4j.api.value.ValueDeserializer; |
| import org.qi4j.api.value.ValueSerializationException; |
| import org.qi4j.functional.Function; |
| import org.qi4j.spi.value.ValueDeserializerAdapter; |
| |
| /** |
| * ValueDeserializer reading Values from JSON documents using Jackson. |
| */ |
| public class JacksonValueDeserializer |
| extends ValueDeserializerAdapter<JsonParser, JsonNode> |
| { |
| |
| private final JsonFactory jsonFactory = new MappingJsonFactory(); |
| |
| public JacksonValueDeserializer( @Structure Application application, |
| @Structure Module module, |
| @Service ServiceReference<ValueDeserializer> serviceRef ) |
| { |
| super( application, module, serviceRef ); |
| } |
| |
| @Override |
| protected JsonParser adaptInput( InputStream input ) |
| throws Exception |
| { |
| return jsonFactory.createParser( input ); |
| } |
| |
| @Override |
| protected void onDeserializationEnd( ValueType valueType, JsonParser input ) |
| throws Exception |
| { |
| input.close(); |
| } |
| |
| @Override |
| protected Object readPlainValue( JsonParser input ) |
| throws Exception |
| { |
| JsonNode jsonNode = input.readValueAsTree(); |
| if( jsonNode.isArray() || jsonNode.isObject() ) |
| { |
| throw new ValueSerializationException( "Asked for a Value but found an Object or an Array at " |
| + input.getCurrentLocation().toString() ); |
| } |
| if( jsonNode.isDouble() ) |
| { |
| return jsonNode.asDouble(); |
| } |
| if( jsonNode.isLong() ) |
| { |
| return jsonNode.asLong(); |
| } |
| if( jsonNode.isInt() ) |
| { |
| return jsonNode.asInt(); |
| } |
| if( jsonNode.isBoolean() ) |
| { |
| return jsonNode.asBoolean(); |
| } |
| if( jsonNode.isNull() ) |
| { |
| return null; |
| } |
| return jsonNode.asText(); |
| } |
| |
| @Override |
| protected <T> Collection<T> readArrayInCollection( JsonParser input, |
| Function<JsonParser, T> deserializer, |
| Collection<T> collection ) |
| throws Exception |
| { |
| JsonToken token = input.getCurrentToken(); |
| if( token == JsonToken.VALUE_NULL ) |
| { |
| return null; |
| } |
| if( token != JsonToken.START_ARRAY ) |
| { |
| token = input.nextToken(); |
| } |
| if( token == JsonToken.VALUE_NULL ) |
| { |
| return null; |
| } |
| if( token != JsonToken.START_ARRAY ) |
| { |
| throw new ValueSerializationException( "Expected an array start at " |
| + input.getCurrentLocation().toString() ); |
| } |
| while( input.nextToken() != JsonToken.END_ARRAY ) |
| { |
| T element = deserializer.map( input ); |
| collection.add( element ); |
| } |
| return collection; |
| } |
| |
| @Override |
| protected <K, V> Map<K, V> readMapInMap( JsonParser input, |
| Function<JsonParser, K> keyDeserializer, |
| Function<JsonParser, V> valueDeserializer, |
| Map<K, V> map ) |
| throws Exception |
| { |
| JsonToken token = input.getCurrentToken(); |
| if( token == JsonToken.VALUE_NULL ) |
| { |
| return null; |
| } |
| if( token != JsonToken.START_ARRAY ) |
| { |
| token = input.nextToken(); |
| } |
| if( token == JsonToken.VALUE_NULL ) |
| { |
| return null; |
| } |
| if( token != JsonToken.START_ARRAY ) |
| { |
| throw new ValueSerializationException( "Expected an array start at " |
| + input.getCurrentLocation().toString() ); |
| } |
| JsonToken currentToken = input.nextToken(); |
| while( currentToken != JsonToken.END_ARRAY ) |
| { |
| if( currentToken != JsonToken.START_OBJECT ) |
| { |
| throw new ValueSerializationException( "Expected an object start at " |
| + input.getCurrentLocation().toString() ); |
| } |
| currentToken = input.nextToken(); |
| K key = null; |
| V value = null; |
| while( currentToken != JsonToken.END_OBJECT ) |
| { |
| String objectKey = input.getCurrentName(); |
| input.nextToken(); |
| if( "key".equals( objectKey ) ) |
| { |
| key = keyDeserializer.map( input ); |
| } |
| else if( "value".equals( objectKey ) ) |
| { |
| value = valueDeserializer.map( input ); |
| } |
| else |
| { |
| //input.nextToken(); |
| input.skipChildren(); |
| } |
| currentToken = input.nextToken(); |
| } |
| if( key != null ) |
| { |
| map.put( key, value ); |
| } |
| currentToken = input.nextToken(); |
| } |
| return map; |
| } |
| |
| @Override |
| protected ObjectNode readObjectTree( JsonParser input ) |
| throws Exception |
| { |
| JsonToken token = input.getCurrentToken(); |
| if( token == JsonToken.VALUE_NULL ) |
| { |
| return null; |
| } |
| if( token != JsonToken.START_OBJECT ) |
| { |
| token = input.nextToken(); |
| } |
| if( token != JsonToken.START_OBJECT ) |
| { |
| throw new ValueSerializationException( "Expected an object start at " |
| + input.getCurrentLocation().toString() ); |
| } |
| return (ObjectNode) input.readValueAsTree(); |
| } |
| |
| @Override |
| protected Object asSimpleValue( JsonNode inputNode ) |
| throws Exception |
| { |
| if( isNullOrMissing( inputNode ) ) |
| { |
| return null; |
| } |
| if( !inputNode.isValueNode() ) |
| { |
| throw new ValueSerializationException( "Expected a value node but got a container node " + inputNode ); |
| } |
| if( inputNode.isDouble() ) |
| { |
| return inputNode.asDouble(); |
| } |
| if( inputNode.isLong() ) |
| { |
| return inputNode.asLong(); |
| } |
| if( inputNode.isInt() ) |
| { |
| return inputNode.asInt(); |
| } |
| if( inputNode.isBoolean() ) |
| { |
| return inputNode.asBoolean(); |
| } |
| return inputNode.asText(); |
| } |
| |
| @Override |
| protected boolean isObjectValue( JsonNode inputNode ) |
| throws Exception |
| { |
| if( isNullOrMissing( inputNode ) ) |
| { |
| return false; |
| } |
| return inputNode.isObject(); |
| } |
| |
| @Override |
| protected boolean objectHasField( JsonNode inputNode, String key ) |
| throws Exception |
| { |
| if( isNullOrMissing( inputNode ) ) |
| { |
| return false; |
| } |
| if( !inputNode.isObject() ) |
| { |
| throw new ValueSerializationException( "Expected an object but got " + inputNode ); |
| } |
| return inputNode.has( key ); |
| } |
| |
| @Override |
| protected <T> T getObjectFieldValue( JsonNode inputNode, String key, Function<JsonNode, T> valueDeserializer ) |
| throws Exception |
| { |
| JsonNode valueNode = inputNode.get( key ); |
| if( isNullOrMissing( valueNode ) ) |
| { |
| return null; |
| } |
| T value = valueDeserializer.map( valueNode ); |
| return value; |
| } |
| |
| @Override |
| protected <T> void putArrayNodeInCollection( JsonNode inputNode, |
| Function<JsonNode, T> deserializer, |
| Collection<T> collection ) |
| throws Exception |
| { |
| if( isNullOrMissing( inputNode ) ) |
| { |
| return; |
| } |
| if( !inputNode.isArray() ) |
| { |
| throw new ValueSerializationException( "Expected an array but got " + inputNode ); |
| } |
| ArrayNode array = (ArrayNode) inputNode; |
| for( JsonNode item : array ) |
| { |
| T value = deserializer.map( item ); |
| collection.add( value ); |
| } |
| } |
| |
| @Override |
| protected <K, V> void putArrayNodeInMap( JsonNode inputNode, |
| Function<JsonNode, K> keyDeserializer, |
| Function<JsonNode, V> valueDeserializer, |
| Map<K, V> map ) |
| throws Exception |
| { |
| if( isNullOrMissing( inputNode ) ) |
| { |
| return; |
| } |
| if( !inputNode.isArray() ) |
| { |
| throw new ValueSerializationException( "Expected an array but got " + inputNode ); |
| } |
| ArrayNode array = (ArrayNode) inputNode; |
| for( JsonNode item : array ) |
| { |
| if( !item.isObject() ) |
| { |
| throw new ValueSerializationException( "Expected an object but got " + inputNode ); |
| } |
| JsonNode keyNode = item.get( "key" ); |
| JsonNode valueNode = item.get( "value" ); |
| K key = keyDeserializer.map( keyNode ); |
| V value = valueDeserializer.map( valueNode ); |
| if( key != null ) |
| { |
| map.put( key, value ); |
| } |
| } |
| } |
| |
| @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(); |
| } |
| } |