001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.jackson; 018 019import java.io.IOException; 020import java.math.BigDecimal; 021import java.math.BigInteger; 022 023import com.fasterxml.jackson.core.JsonParser; 024import com.fasterxml.jackson.core.ObjectCodec; 025import com.fasterxml.jackson.core.TreeNode; 026import com.fasterxml.jackson.databind.DeserializationContext; 027import com.fasterxml.jackson.databind.JsonDeserializer; 028import com.fasterxml.jackson.databind.JsonMappingException; 029import com.fasterxml.jackson.databind.JsonNode; 030import com.fasterxml.jackson.databind.node.NullNode; 031 032import org.springframework.util.Assert; 033 034/** 035 * Helper base class for {@link JsonDeserializer} implementations that deserialize 036 * objects. 037 * 038 * @param <T> the supported object type 039 * @author Phillip Webb 040 * @since 1.4.0 041 * @see JsonObjectSerializer 042 */ 043public abstract class JsonObjectDeserializer<T> 044 extends com.fasterxml.jackson.databind.JsonDeserializer<T> { 045 046 @Override 047 public final T deserialize(JsonParser jp, DeserializationContext ctxt) 048 throws IOException { 049 try { 050 ObjectCodec codec = jp.getCodec(); 051 JsonNode tree = codec.readTree(jp); 052 return deserializeObject(jp, ctxt, codec, tree); 053 } 054 catch (Exception ex) { 055 if (ex instanceof IOException) { 056 throw (IOException) ex; 057 } 058 throw new JsonMappingException(jp, "Object deserialize error", ex); 059 } 060 } 061 062 /** 063 * Deserialize JSON content into the value type this serializer handles. 064 * @param jsonParser the source parser used for reading JSON content 065 * @param context context that can be used to access information about this 066 * deserialization activity 067 * @param codec the {@link ObjectCodec} associated with the parser 068 * @param tree deserialized JSON content as tree expressed using set of 069 * {@link TreeNode} instances 070 * @return the deserialized object 071 * @throws IOException on error 072 * @see #deserialize(JsonParser, DeserializationContext) 073 */ 074 protected abstract T deserializeObject(JsonParser jsonParser, 075 DeserializationContext context, ObjectCodec codec, JsonNode tree) 076 throws IOException; 077 078 /** 079 * Helper method to extract a value from the given {@code jsonNode} or return 080 * {@code null} when the node itself is {@code null}. 081 * @param jsonNode the source node (may be {@code null}) 082 * @param type the data type. May be {@link String}, {@link Boolean}, {@link Long}, 083 * {@link Integer}, {@link Short}, {@link Double}, {@link Float}, {@link BigDecimal} 084 * or {@link BigInteger}. 085 * @param <D> the data type requested 086 * @return the node value or {@code null} 087 */ 088 @SuppressWarnings({ "unchecked" }) 089 protected final <D> D nullSafeValue(JsonNode jsonNode, Class<D> type) { 090 Assert.notNull(type, "Type must not be null"); 091 if (jsonNode == null) { 092 return null; 093 } 094 if (type == String.class) { 095 return (D) jsonNode.textValue(); 096 } 097 if (type == Boolean.class) { 098 return (D) Boolean.valueOf(jsonNode.booleanValue()); 099 } 100 if (type == Long.class) { 101 return (D) Long.valueOf(jsonNode.longValue()); 102 } 103 if (type == Integer.class) { 104 return (D) Integer.valueOf(jsonNode.intValue()); 105 } 106 if (type == Short.class) { 107 return (D) Short.valueOf(jsonNode.shortValue()); 108 } 109 if (type == Double.class) { 110 return (D) Double.valueOf(jsonNode.doubleValue()); 111 } 112 if (type == Float.class) { 113 return (D) Float.valueOf(jsonNode.floatValue()); 114 } 115 if (type == BigDecimal.class) { 116 return (D) jsonNode.decimalValue(); 117 } 118 if (type == BigInteger.class) { 119 return (D) jsonNode.bigIntegerValue(); 120 } 121 throw new IllegalArgumentException("Unsupported value type " + type.getName()); 122 } 123 124 /** 125 * Helper method to return a {@link JsonNode} from the tree. 126 * @param tree the source tree 127 * @param fieldName the field name to extract 128 * @return the {@link JsonNode} 129 */ 130 protected final JsonNode getRequiredNode(JsonNode tree, String fieldName) { 131 Assert.notNull(tree, "Tree must not be null"); 132 JsonNode node = tree.get(fieldName); 133 Assert.state(node != null && !(node instanceof NullNode), 134 () -> "Missing JSON field '" + fieldName + "'"); 135 return node; 136 } 137 138}