2014-11-05 129 views
0

我使用杰克逊2.4来转换POJO /从地图。我写了一个小测试程序如下。Java杰克逊默认类型映射

import com.fasterxml.jackson.databind.ObjectMapper; 
import java.util.Map; 

public class TestObjectMapper { 

    private Object byteVal; 
    private Object shortVal; 
    private Object intVal; 
    private Object longVal; 
    private Object floatVal; 
    private Object doubleVal; 

    public TestObjectMapper() { 
     byteVal = (byte) 127; 
     shortVal = (short) 255; 
     intVal = 10; 
     longVal = 20L; 
     floatVal = 1.2F; 
     doubleVal = 1.4; 
     System.out.println("Constructor"); 
     System.out.println("byteVal.getClass() = " + byteVal.getClass()); 
     System.out.println("shortVal.getClass() = " + shortVal.getClass()); 
     System.out.println("intVal.getClass() = " + intVal.getClass()); 
     System.out.println("longVal.getClass() = " + longVal.getClass()); 
     System.out.println("floatVal.getClass() = " + floatVal.getClass()); 
     System.out.println("doubleVal.getClass() = " + doubleVal.getClass()); 
     System.out.println(); 
    } 

    public Object getByteVal() { 
     return byteVal; 
    } 

    public Object getShortVal() { 
     return shortVal; 
    } 

    public Object getIntVal() { 
     return intVal; 
    } 

    public Object getLongVal() { 
     return longVal; 
    } 

    public Object getFloatVal() { 
     return floatVal; 
    } 

    public Object getDoubleVal() { 
     return doubleVal; 
    } 

    public static void main(String[] args) { 
     ObjectMapper mapper = new ObjectMapper(); 
     TestObjectMapper t = new TestObjectMapper(); 
     Map map = mapper.convertValue(t, Map.class); 
     System.out.println("map = " + map); 
     System.out.println(); 
     for (Object key : map.keySet()) { 
      System.out.format("map.get(\"%s\").getClass() = %s\n", key, map.get(key).getClass()); 
     } 
     String k = "byteVal"; 
     System.out.format("((Integer) map.get(\"%s\")).byteValue() = %d\n", 
       k, ((Integer) map.get(k)).byteValue()); 
     k = "floatVal"; 
     System.out.format("((Double) map.get(\"%s\")).floatValue() = %f\n", 
       k, ((Double) map.get(k)).floatValue()); 
    } 

} 

产生以下输出:

Constructor 
byteVal.getClass() = class java.lang.Byte 
shortVal.getClass() = class java.lang.Short 
intVal.getClass() = class java.lang.Integer 
longVal.getClass() = class java.lang.Long 
floatVal.getClass() = class java.lang.Float 
doubleVal.getClass() = class java.lang.Double 

map = {byteVal=127, shortVal=255, intVal=10, longVal=20, floatVal=1.2000000476837158, doubleVal=1.4} 

map.get("byteVal").getClass() = class java.lang.Integer 
map.get("shortVal").getClass() = class java.lang.Short 
map.get("intVal").getClass() = class java.lang.Integer 
map.get("longVal").getClass() = class java.lang.Long 
map.get("floatVal").getClass() = class java.lang.Double 
map.get("doubleVal").getClass() = class java.lang.Double 
((Integer) map.get("byteVal")).byteValue() = 127 
((Double) map.get("floatVal")).floatValue() = 1.200000 

为什么类型在某些情况下,正确的,但不是在别人的映射?有没有办法控制这个,而不需要对我的类做任何改变?

+4

的源代码你所说的 “正确” 是什么意思?您是否希望杰克逊在您的领域没有声明任何特定类型时能够阅读您的想法? – chrylis 2014-11-05 08:50:52

+0

你能否澄清一下问题?究竟是什么问题? – 2014-11-05 09:00:04

+0

差异是Byte-> Integer和Float-> Double。 Json中没有Byte类型。所以Float和Byte将被转换为JSON中的Number类型。 请参阅http://www.tutorialspoint.com/json/json_data_types.htm,因此Number是JavaScript中的双精度浮点格式 – nomoa 2014-11-05 09:23:26

回答

2

为什么类型的映射在某些情况下是正确的,但在其他情况下却不正确?

这是从Jackson预计的行为。如果考虑到JSON支持的数据类型(如下所示),那么Jackson将相应地转换值的数据类型是完全合理的。

  • Number - 可能包含一个小数部分,可以使用指数E符号有符号十进制数。 JSON不允许像NaN这样的非数字,也不会在整数和浮点之间做任何区分。 (即使JavaScript对其所有数字值使用双精度浮点格式,实现JSON的其他语言也可能对数字编码的方式不同)
  • String - 零个或多个Unicode字符序列,尽管BMP之外的字符必须被表示为代理对。字符串用双引号分隔,并支持反斜杠转义语法。
  • Boolean - 任一值的真或假
  • Array - 零个或更多个值,其中的每一个可以是任何类型的一个有序列表。数组以逗号分隔的元素使用方括号表示法。
  • Object - 一个无序的关联数组(名称/值对)。对象使用大括号分隔,并使用逗号分隔每对,而在每对中,冒号“:”字符将键或名称与其值分开。所有的键都必须是字符串,并且在该对象内应该彼此不同。
  • null - 一个空值时,用的是空

有控制这个没有做我的 类的任何改变的一种方式?

它可以控制,而杰克逊是用来反序列化,但不是在序列化时。以下堆栈溢出答案可能对你有一些帮助。

Java Jackson - prevent float to int conversion when deserializing

UPDATE

为了一个对象转换为另一种,ObjectMapper第一串行化所述源对象到JsonParser对象,然后反序列化此JsonParser对象到目标对象类型。因此,它使得ObjectMapper的这种行为非常明显。

/

** 
    * Actual conversion implementation: instead of using existing read 
    * and write methods, much of code is inlined. Reason for this is 
    * that we must avoid wrapping/unwrapping both for efficiency and 
    * for correctness. If wrapping/unwrapping is actually desired, 
    * caller must use explicit <code>writeValue</code> and 
    * <code>readValue</code> methods. 
    */ 
    protected Object _convert(Object fromValue, JavaType toValueType) 
     throws IllegalArgumentException 
    { 
     // sanity check for null first: 
     if (fromValue == null) return null; 
     /* Then use TokenBuffer, which is a JsonGenerator: 
     * (see [JACKSON-175]) 
     */ 
     TokenBuffer buf = new TokenBuffer(this); 
     try { 
      // inlined 'writeValue' with minor changes: 
      // first: disable wrapping when writing 
      SerializationConfig config = getSerializationConfig().without(SerializationFeature.WRAP_ROOT_VALUE); 
      // no need to check for closing of TokenBuffer 
      _serializerProvider(config).serializeValue(buf, fromValue); 

      // then matching read, inlined 'readValue' with minor mods: 
      final JsonParser jp = buf.asParser(); 
      Object result; 
      // ok to pass in existing feature flags; unwrapping handled by mapper 
      final DeserializationConfig deserConfig = getDeserializationConfig(); 
      JsonToken t = _initForReading(jp); 
      if (t == JsonToken.VALUE_NULL) { 
       DeserializationContext ctxt = createDeserializationContext(jp, deserConfig); 
       result = _findRootDeserializer(ctxt, toValueType).getNullValue(); 
      } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { 
       result = null; 
      } else { // pointing to event other than null 
       DeserializationContext ctxt = createDeserializationContext(jp, deserConfig); 
       JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, toValueType); 
       // note: no handling of unwarpping 
       result = deser.deserialize(jp, ctxt); 
      } 
      jp.close(); 
      return result; 
     } catch (IOException e) { // should not occur, no real i/o... 
      throw new IllegalArgumentException(e.getMessage(), e); 
     } 
    } 

http://grepcode.com/file/repo1.maven.org/maven2/com.fasterxml.jackson.core/jackson-databind/2.0.0-RC1/com/fasterxml/jackson/databind/ObjectMapper.java#ObjectMapper.convertValue%28java.lang.Object%2Cjava.lang.Class%29

+0

我从原始问题中删除了JSON,因为我意识到它在这里并不真正相关。我只是在谈论地图。 – Peter 2014-11-05 10:22:06

+0

更新了我的答案。 – 2014-11-05 11:48:57

+0

谢谢,这说明了为什么映射的工作原理就像它一样。 – Peter 2014-11-05 15:08:45