Jackson配置属性(objectMapper.configure)

 json  Jackson配置属性(objectMapper.configure)已关闭评论
11月 032016
 

Jackson配置属性
如果上面的工具类实例,在Jackson中存在一些属性配置,这些配置决定了最后在解析或者编码后数据视图。因此,在分析Jackson之前,先了解下,Jackson具有的一些配置含义。
JsonParser解析相关配置属性
JsonParser将JSON 数据格式的String字符串,解析成为Java对象。Jackson在解析的时候,对于一些非JSON官方文档支持的属性,则需要通过一些配置才可以被Jackson工具解析成对象。
/**
     * Enumeration that defines all togglable features for parsers.
     */
    public enum Feature {

        // // // Low-level I/O handling features:

        /**
         * 这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源。 如果禁止,则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader;
         * 如果允许,parser只要自己需要获取closed方法(当遇到输入流结束,或者parser自己调用 JsonParder#close方法),就会处理流关闭。
         *
         * 注意:这个属性默认是true,即允许自动关闭流
         *
         */
        AUTO_CLOSE_SOURCE(true),

        // // // Support for non-standard data format constructs

        /**
         * 该特性决定parser将是否允许解析使用Java/C++ 样式的注释(包括’/’+’*’ 和’//’ 变量)。 由于JSON标准说明书上面没有提到注释是否是合法的组成,所以这是一个非标准的特性;
         * 尽管如此,这个特性还是被广泛地使用。
         *
         * 注意:该属性默认是false,因此必须显式允许,即通过JsonParser.Feature.ALLOW_COMMENTS 配置为true。
         *
         */
        ALLOW_COMMENTS(false),

        /**
         * 这个特性决定parser是否将允许使用非双引号属性名字, (这种形式在Javascript中被允许,但是JSON标准说明书中没有)。
         *
         * 注意:由于JSON标准上需要为属性名称使用双引号,所以这也是一个非标准特性,默认是false的。
         * 同样,需要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true,打开该特性。
         *
         */
        ALLOW_UNQUOTED_FIELD_NAMES(false),

        /**
         * 该特性决定parser是否允许单引号来包住属性名称和字符串值。
         *
         * 注意:默认下,该属性也是关闭的。需要设置JsonParser.Feature.ALLOW_SINGLE_QUOTES为true
         *
         */
        ALLOW_SINGLE_QUOTES(false),

        /**
         * 该特性决定parser是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。
         * JSON标准说明书要求所有控制符必须使用引号,因此这是一个非标准的特性。
         *
         * 注意:默认时候,该属性关闭的。需要设置:JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS为true。
         *
         */
        ALLOW_UNQUOTED_CONTROL_CHARS(false),

        /**
         * 该特性可以允许接受所有引号引起来的字符,使用‘反斜杠\’机制:如果不允许,只有JSON标准说明书中 列出来的字符可以被避开约束。
         *
         * 由于JSON标准说明中要求为所有控制字符使用引号,这是一个非标准的特性,所以默认是关闭的。
         *
         * 注意:一般在设置ALLOW_SINGLE_QUOTES属性时,也设置了ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER属性,
         * 所以,有时候,你会看到不设置ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER为true,但是依然可以正常运行。
         *
         * @since 1.6
         */
        ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),

        /**
         * 该特性决定parser是否允许JSON整数以多个0开始(比如,如果000001赋值给json某变量,
         * 如果不设置该属性,则解析成int会抛异常报错:org.codehaus.jackson.JsonParseException: Invalid numeric value: Leading zeroes not
         * allowed)
         *
         * 注意:该属性默认是关闭的,如果需要打开,则设置JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS为true。
         *
         * @since 1.8
         */
        ALLOW_NUMERIC_LEADING_ZEROS(false),

        /**
         * 该特性允许parser可以识别”Not-a-Number” (NaN)标识集合作为一个合法的浮点数。 例如: allows (tokens are quoted contents, not including
         * quotes):
         * <ul>
         * <li>”INF” (for positive infinity), as well as alias of “Infinity”
         * <li>”-INF” (for negative infinity), alias “-Infinity”
         * <li>”NaN” (for other not-a-numbers, like result of division by zero)
         * </ul>
         */

        ALLOW_NON_NUMERIC_NUMBERS(false),

        // // // Controlling canonicalization (interning etc)

        /**
         * 该特性决定JSON对象属性名称是否可以被String#intern 规范化表示。
         *
         * 如果允许,则JSON所有的属性名将会 intern() ;如果不设置,则不会规范化,
         *
         * 默认下,该属性是开放的。此外,必须设置CANONICALIZE_FIELD_NAMES为true
         *
         * 关于intern方法作用:当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串 (该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String
         * 对象添加到池中, 并且返回此 String 对象的引用。
         *
         * @since 1.3
         */
        INTERN_FIELD_NAMES(true),

        /**
         * 该特性决定JSON对象的属性名称是否被规范化。
         *
         * @since 1.5
         */
        CANONICALIZE_FIELD_NAMES(true),

        ;

        final boolean _defaultState;

        /**
         * Method that calculates bit set (flags) of all features that are enabled by default.
         */
        public static int collectDefaults() {
            int flags = 0;
            for (Feature f : values()) {
                if (f.enabledByDefault()) {
                    flags |= f.getMask();
                }
            }
            return flags;
        }

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        public boolean enabledByDefault() {
            return _defaultState;
        }

        public boolean enabledIn(int flags) {
            return (flags & getMask()) != 0;
        }

        public int getMask() {
            return (1 << ordinal());
        }
    };
Note: 在枚举最后有一个公共静态方法collectDefaults(),这个方法返回一个整形,整形包含的是所有枚举项对应位bit为初始默认值(true:1;false:0),如果默认属性为true,则通过对1 << ordinal()的值和flags进行亦或来置位。
DeserializationConfig反序列化相关配置属性
将Java 对象序列化为Json字符串。Jackson在序列化Java对象的时候,对于有些不存在的属性处理,以及一些类型转换等,都可以通过配置来设置。
/**
     * Enumeration that defines togglable features that guide the serialization feature.
     */
    public enum Feature implements MapperConfig.ConfigFeature {
        /*
         * /****************************************************** Introspection features
         * /******************************************************
         */

        /**
         * 该特性决定是否启动内部注解功能支持配置;如果允许,则使用AnnotationIntrospector扫描配置,否则,不考了注解配置。
         *
         * 默认启动该功能配置属性。
         * 
         * @since 1.2
         */
        USE_ANNOTATIONS(true),

        /**
         * 该特性决定是否使用“getter”方法来根据标准bean命名转换方式来自动检测。如果true,则所有公共的带有一个参数
         * 并且前缀为set的方法都将被当做setter方法。如果false,只会把显式注解的作为setter方法。
         *
         * 注意: 这个特性的优先级低于显式注解,并且只会在获取不到更细粒度配置的情况下。
         *
         */
        AUTO_DETECT_GETTERS(true),                        DETECT_IS_GETTERS(true),

        /**
         * 该特性决定是否使用creator方法来根据公共构造函数以及名字为“valueOf”的静态单参数方法自动检测。
         *
         * 注意:这个特性比每个类上注解的优先级要低。
         *
         */
        AUTO_DETECT_CREATORS(true),

        /**
         * 这个特性决定是否非静态field被当做属性。如果true,则所有公共成员field都被当做属性, 否则只有注解,才会被当做属性field。
         *
         */
        AUTO_DETECT_FIELDS(true),

        /**
         * 使用getter方法来作为setter方法(一般只处理集合和Maps,和其他没有setter的类型)。 该属性决定是否不需要setter方法,而只需要getter方法来修改属性。
         * 
         * 注意:该配置优先级低于setter。
         */
        USE_GETTERS_AS_SETTERS(true),

        /**
         * 该特性决定当访问属性时候,方法和field访问是否修改设置。 如果设置为true,则通过反射调用方法AccessibleObject#setAccessible 来允许访问不能访问的对象。
         * 
         */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),                GETTERS(false),

        /*
         * /****************************************************** /* Type conversion features
         * /******************************************************
         */

        /**
         * 该特性决定对于json浮点数,是否使用BigDecimal来序列化。如果不允许,则使用Double序列化。
         * 
         * 注意:该特性默认是关闭的,因为性能上来说,BigDecimal低于Double。
         *
         */
        USE_BIG_DECIMAL_FOR_FLOATS(false),

        /**
         * 该特性决定对于json整形(非浮点),是否使用BigInteger来序列化。如果不允许,则根据数值大小来确定 是使用Integer}, {@link Long} 或者
         * {@link java.math.BigInteger}
         *
         */
        USE_BIG_INTEGER_FOR_INTS(false),

        // [JACKSON-652]
        /**
         * 该特性决定JSON ARRAY是映射为Object[]还是List<Object>。如果开启,都为Object[],false时,则使用List。
         *
         * @since 1.9
         */
        USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),

        /**
         * 该特性决定了使用枚举值的标准序列化机制:如果允许,则枚举假定使用Enum.toString()返回的值作为序列化结构;如果禁止, 则返回Enum.name()的值。
         *
         * 注意:默认使用的时Enum.name()的值作为枚举序列化结果。这个的设置和WRITE_ENUMS_USING_TO_STRING需要一致。
         *
         * For further details, check out [JACKSON-212]
         * 
         * @since 1.6
         */
        READ_ENUMS_USING_TO_STRING(false),

        /*
         * /****************************************************** Error handling features
         * /****************************************************** 错误处理特性
         */

        /**
         * 该特性决定了当遇到未知属性(没有映射到属性,没有任何setter或者任何可以处理它的handler),是否应该抛出一个
         * JsonMappingException异常。这个特性一般式所有其他处理方法对未知属性处理都无效后才被尝试,属性保留未处理状态。
         *
         * 默认情况下,该设置是被打开的。
         *
         * @since 1.2
         */
        FAIL_ON_UNKNOWN_PROPERTIES(true),

        /**
         * 该特性决定当遇到JSON null的对象是java 原始类型,则是否抛出异常。当false时,则使用0 for ‘int’, 0.0 for double 来设定原始对象初始值。
         *
         * 默认情况下,允许原始类型可以使用null。
         *
         * @since 1.7
         */
        FAIL_ON_NULL_FOR_PRIMITIVES(false),

        /**
         * 该特性决定JSON 整数是否是一个有效的值,当被用来反序列化Java枚举值。如果false,数字可以接受,并且映射为枚举的值ordinal();
         * 如果true,则数字不允许并且抛出JsonMappingException异常。后面一种行为原因是因为大部分情况下,枚举被反序列化为 JSON 字符串, 从而造成从整形到枚举的意外映射关系。
         *
         * Feature is disabled by default (to be consistent with behavior of Jackson 1.6), i.e. to allow use of JSON
         * integers for Java enums.
         * 
         * @since 1.7
         */
        FAIL_ON_NUMBERS_FOR_ENUMS(false),

        /**
         * 异常封装,不封装Error,catch异常之后,抛出IOException。默认封装异常。
         *
         * @since 1.7
         */
        WRAP_EXCEPTIONS(true),

        /*
         * /****************************************************** Structural conversion features
         * /****************************************************** 数据结构转换特性
         */

        /**
         * 该特性决定是否接受强制非数组(JSON)值到Java集合类型。如果允许,集合反序列化将尝试处理非数组值。
         *
         * Feature that determines whether it is acceptable to coerce non-array (in JSON) values to work with Java
         * collection (arrays, java.util.Collection) types. If enabled, collection deserializers will try to handle
         * non-array values as if they had “implicit” surrounding JSON array. This feature is meant to be used for
         * compatibility/interoperability reasons, to work with packages (such as XML-to-JSON converters) that leave out
         * JSON array in cases where there is just a single element in array.
         * 
         * @since 1.8
         */
        ACCEPT_SINGLE_VALUE_AS_ARRAY(false),

        /**
         * 该特征允许 unwrap根级别JSON 值,来匹配WRAP_ROOT_VALUE 序列化设置。
         *
         * @since 1.9
         */
        UNWRAP_ROOT_VALUE(false),

        /*
         * /****************************************************** Value conversion features
         * /****************************************************** 值转换特性
         */

        /**
         * 该特性可以允许JSON空字符串转换为POJO对象为null。如果禁用,则标准POJO只会从JSON null或者JSON对象转换过来;
         * 如果允许,则空JSON字符串可以等价于JSON null。
         * @since 1.8
         */
        ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false)

        ;

        final boolean _defaultState;

        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() {
            return _defaultState;
        }

        @Override
        public int getMask() {
            return (1 << ordinal());
        }
    }
SerializationConfig 序列化相关配置属性
/**
     * 定义序列化对象所需配置的一些枚举.
     */
    public enum Feature implements MapperConfig.ConfigFeature
    {
        /*
        /******************************************************
        /*  Introspection features
        /******************************************************
         */
        
        /**
         * 注解扫描配置
         */
        USE_ANNOTATIONS(true),

        /**
         * 获取getter方法,前缀为get
         */
        AUTO_DETECT_GETTERS(true),

        /**
         * 获取getter方法,前缀为is
         */
        AUTO_DETECT_IS_GETTERS(true),

        /**
         * 将对象所有的field作为json属性
         */
         AUTO_DETECT_FIELDS(true),

        /**
         * 该特性决定当访问属性时候,方法和field访问是否修改设置。 如果设置为true,
         * 则通过反射调用方法AccessibleObject#setAccessible 来允许访问不能访问的对象。
         */
        CAN_OVERRIDE_ACCESS_MODIFIERS(true),

        /**
         * 获取的getter方法需要setter方法,否则,所有发现的getter都可以作为getter方法。
         */
        REQUIRE_SETTERS_FOR_GETTERS(false),
        
        /*
        /******************************************************
        /* Generic output features
        /******************************************************
         */

        /**
         * 属性对应的值为null,是否需要写出来,write out。
         */
        @Deprecated
        WRITE_NULL_PROPERTIES(true),

        /**
         * 特征决定是使用运行时动态类型,还是声明的静态类型。
         * 也可以使用{@link JsonSerialize#typing} 注解属性
         */
        USE_STATIC_TYPING(false),

        /**
         * 该特性决定拥有view注解{@link org.codehaus.jackson.map.annotate.JsonView}的属性是否在JSON序列化视图中。如果true,则非注解视图,也包含;
         * 否则,它们将会被排除在外。
         *
         */
        DEFAULT_VIEW_INCLUSION(true),
        
        /**
         * 在JAVA中配置XML root{@XmlRootElement.name}注解,最后xml数据中会出现对应root根name.
         */
        WRAP_ROOT_VALUE(false),

        /**
         * 该特性对于最基础的生成器,使用默认pretty printer {@link org.codehaus.jackson.JsonGenerator#useDefaultPrettyPrinter}
         * 这只会对{@link org.codehaus.jackson.JsonGenerator}有影响.该属性值允许使用默认的实现。
         */
        INDENT_OUTPUT(false),

        /**
         * 是否对属性使用排序,默认排序按照字母顺序。
         */
        SORT_PROPERTIES_ALPHABETICALLY(false),
        
        /*
        /******************************************************
        /*  Error handling features
        /******************************************************
         */
        
        /**
         * 是否允许一个类型没有注解表明打算被序列化。默认true,抛出一个异常;否则序列化一个空对象,比如没有任何属性。
         *
         * Note that empty types that this feature has only effect on
         * those “empty” beans that do not have any recognized annotations
         * (like <code>@JsonSerialize</code>): ones that do have annotations
         * do not result in an exception being thrown.
         *
         * @since 1.4
         */
        FAIL_ON_EMPTY_BEANS(true),

        /**
         * 封装所有异常
         */
        WRAP_EXCEPTIONS(true),

        /*
        /******************************************************
        /* Output life cycle features
        /******************************************************
         */
        
         /**
          * 该特性决定序列化root级对象的实现closeable接口的close方法是否在序列化后被调用。
          * 
          * 注意:如果true,则完成序列化后就关闭;如果,你可以在处理最后,调用排序操作等,则为false。
          * 
          */
        CLOSE_CLOSEABLE(false),

        /**
         * 该特性决定是否在writeValue()方法之后就调用JsonGenerator.flush()方法。
         * 当我们需要先压缩,然后再flush,则可能需要false。
         * 
         */
        FLUSH_AFTER_WRITE_VALUE(true),
         
        /*
        /******************************************************
        /* Data type – specific serialization configuration
        /******************************************************
         */

        /**
         * 该特性决定是否将基于Date的值序列化为timestamp数字式的值,或者作为文本表示。
         * 如果文本表示,则实际格式化的时候会调用{@link #getDateFormat}方法。
         * 
         * 该特性可能会影响其他date相关类型的处理,虽然我们理想情况是只对date起作用。
         * 
         */
        WRITE_DATES_AS_TIMESTAMPS(true),

        /**
         * 是否将Map中得key为Date的值,也序列化为timestamps形式(否则,会被序列化为文本形式的值)。
         */
        WRITE_DATE_KEYS_AS_TIMESTAMPS(false),

        /**
         * 该特性决定怎样处理类型char[]序列化,是否序列化为一个显式的JSON数组,还是默认作为一个字符串。
         *
         */
        WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),

        /**
         * 该特性决定对Enum 枚举值使用标准的序列化机制。如果true,则返回Enum.toString()值,否则为Enum.name()
         *
         */
        WRITE_ENUMS_USING_TO_STRING(false),

        /**
         * 这个特性决定Java枚举值是否序列化为数字(true)或者文本值(false).如果是值的话,则使用Enum.ordinal().
         * 该特性优先级高于上面的那个。
         * 
         * @since 1.9
         */
        WRITE_ENUMS_USING_INDEX(false),
        
        /**
         * 决定是否Map的带有null值的entry被序列化(true)
         *
         */
        WRITE_NULL_MAP_VALUES(true),

        /**
         * 决定容器空的属性(声明为Collection或者array的值)是否被序列化为空的JSON数组(true),否则强制输出。
         *
         * Note that this does not change behavior of {@link java.util.Map}s, or
         * “Collection-like” types.
         * 
         * @since 1.9
         */
        WRITE_EMPTY_JSON_ARRAYS(true)
        
            ;

        final boolean _defaultState;
        
        private Feature(boolean defaultState) {
            _defaultState = defaultState;
        }

        @Override
        public boolean enabledByDefault() { return _defaultState; }
    
        @Override
        public int getMask() { return (1 << ordinal()); }
    }

JSON类库Jackson、JSON-lib、gson性能对比

 json  JSON类库Jackson、JSON-lib、gson性能对比已关闭评论
11月 032016
 

以下数据来自:http://wangym.iteye.com/blog/738933

Jackson:http://jackson.codehaus.org/

JSON-lib:http://json-lib.sourceforge.net/

Gson:http://code.google.com/p/google-gson/

 

测试环境:

 

1、工作电脑:Intel双核E8400 共6GHz,内存4GB,WinXP

2、JSON-lib用最新的JDK15,GSON版本是最新的v1.4,Jackson也是最新的v1.5.5,JDK-v1.6.0_20,JMeter-v2.4

3、测试时不开启任何无关进程,每完成一项测试后关闭JMeter整理内存后,再进行下一项测试,每项测试运行3次,取平均值

4、JSON转Java Bean意为将JSON格式转换成Java类,这个类内包括Map、List、Date、Integer/Long/Double、String等类型的属性,Java Bean转Json则同理。另外,两者互转,每次转换的数据都是随机生成

 

测试结果:

 

* 吞吐量的值越大越好,总耗时的值越小越好

 

JSON转Bean,5个线程并发,约200字节对象,1千万次转换:

 

Jackson JSON-lib Gson
TPS 64113.7 8067.4 13952.8
总耗时(秒) 155 1238 700

 

Bean转JSON,5个线程并发,约200字节对象,1千万次转换:

 

Jackson JSON-lib Gson
TPS 54802 15093.2 17308.2
总耗时(秒) 181 661 560

 

JSON转Bean,5个线程并发,约2K对象,1千万次转换:

 

Jackson JSON-lib Gson
TPS 37314 2406.9 3657.50
总耗时(秒) 267 4120 2720

 

Bean转JSON,5个线程并发,约2K对象,1千万次转换:

 

Jackson JSON-lib Gson
TPS 30922.2 4274.8 4977.00
总耗时(秒) 322 2320 2000

 

测试总结:

 

1、显而易见,无论是哪种形式的转换,Jackson > Gson > Json-lib。

     Jackson的处理能力甚至高出Json-lib有10倍左右

2、JSON-lib似乎已经停止更新,最新的版本也是基于JDK15,而Jackson的社区则较为活跃;

3、在测试性能的同时,又以人肉方式对这三个类库转换的正确性 进行了检查 ,三者均达100%正确 

4、JSON-lib在转换诸如Date类型时较为累赘,如以下是两者的转换结果:

JSON-lib:

{“brithday”:{“date”:17,”day”:2,”hours”:9,”minutes”:24,”month”:7,”seconds”:26,”time”:1282008266398,”timezoneOffset”:-480,”year”:110}}

Jackson:

{“brithday”:1282008123101}

5、JSON-lib依赖commons系列的包及 ezmorph包共 5个,而Jackson除自身的以外只依赖于commons-logging 
6、Jackson提供完整基于节点的Tree Model,以及完整的OJM数据绑定功能。

 

Jackson使用示例:

 

JacksonMapper:

创建为饿汉式单例模式 ,Jackson用于转换的核心类ObjectMapper无需每次都new一个object,官网上的一句话:can reuse, share globally

 

Java代码  收藏代码

  1. /** 
  2.  * @author xuanyin 
  3.  *  
  4.  */  
  5. public class JacksonMapper {  
  6.   
  7.     /** 
  8.      *  
  9.      */  
  10.     private static final ObjectMapper mapper = new ObjectMapper();  
  11.   
  12.     /** 
  13.      *  
  14.      */  
  15.     private JacksonMapper() {  
  16.   
  17.     }  
  18.   
  19.     /** 
  20.      *  
  21.      * @return 
  22.      */  
  23.     public static ObjectMapper getInstance() {  
  24.   
  25.         return mapper;  
  26.     }  
  27.   
  28. }  

 

JSON转Bean:

 

Java代码  收藏代码

  1. ……  
  2. String json = “…”;  
  3. ObjectMapper mapper = JacksonMapper.getInstance();  
  4. YourBean bean = mapper.readValue(json, new YourBean().getClass());  
  5. ……  

 

Bean转JSON:

 

Java代码  收藏代码

  1. ……  
  2. YourBean bean = new YourBean();  
  3. ……  
  4. ObjectMapper mapper = JacksonMapper.getInstance();  
  5. StringWriter sw = new StringWriter();  
  6. JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);  
  7. mapper.writeValue(gen, bean);  
  8. gen.close();  
  9. String json = sw.toString();  
  10. ……  

 

* 上面两段代码中的YourBean当然也可以是Java的基本类型

***************************************************************************************************************

附jackon具体使用:

一、入门
Jackson中有个ObjectMapper类很是实用,用于Java对象与JSON的互换。
1.JAVA对象转JSON[JSON序列化]

import java.io.IOException; 
import java.text.ParseException; 
import java.text.SimpleDateFormat; 
  
import com.fasterxml.jackson.databind.ObjectMapper; 
  
public class JacksonDemo { 
  public static void main(String[] args) throws ParseException, IOException { 
    User user = new User(); 
    user.setName(“小民”);  
    user.setEmail(“xiaomin@sina.com”); 
    user.setAge(20); 
      
    SimpleDateFormat dateformat = new SimpleDateFormat(“yyyy-MM-dd”); 
    user.setBirthday(dateformat.parse(“1996-10-01”));     
      
    /** 
     * ObjectMapper是JSON操作的核心,Jackson的所有JSON操作都是在ObjectMapper中实现。 
     * ObjectMapper有多个JSON序列化的方法,可以把JSON字符串保存File、OutputStream等不同的介质中。 
     * writeValue(File arg0, Object arg1)把arg1转成json序列,并保存到arg0文件中。 
     * writeValue(OutputStream arg0, Object arg1)把arg1转成json序列,并保存到arg0输出流中。 
     * writeValueAsBytes(Object arg0)把arg0转成json序列,并把结果输出成字节数组。 
     * writeValueAsString(Object arg0)把arg0转成json序列,并把结果输出成字符串。 
     */
    ObjectMapper mapper = new ObjectMapper(); 
      
    //User类转JSON 
    //输出结果:{“name”:”小民”,”age”:20,”birthday”:844099200000,”email”:”xiaomin@sina.com”} 
    String json = mapper.writeValueAsString(user); 
    System.out.println(json); 
      
    //Java集合转JSON 
    //输出结果:[{“name”:”小民”,”age”:20,”birthday”:844099200000,”email”:”xiaomin@sina.com”}] 
    List<User> users = new ArrayList<User>(); 
    users.add(user); 
    String jsonlist = mapper.writeValueAsString(users); 
    System.out.println(jsonlist); 
  } 

2.JSON转Java类[JSON反序列化]

import java.io.IOException; 
import java.text.ParseException; 
import com.fasterxml.jackson.databind.ObjectMapper; 
  
public class JacksonDemo { 
  public static void main(String[] args) throws ParseException, IOException { 
    String json = “{\”name\”:\”小民\”,\”age\”:20,\”birthday\”:844099200000,\”email\”:\”xiaomin@sina.com\”}”; 
      
    /** 
     * ObjectMapper支持从byte[]、File、InputStream、字符串等数据的JSON反序列化。 
     */
    ObjectMapper mapper = new ObjectMapper(); 
    User user = mapper.readValue(json, User.class); 
    System.out.println(user); 
  } 

 
二、Jackson支持3种使用方式:
1、Data Binding:最方便使用.
(1)Full Data Binding:

private static final String MODEL_BINDING = “{\”name\”:\”name1\”,\”type\”:1}”; 
  public void fullDataBinding() throws Exception{ 
    ObjectMapper mapper = new ObjectMapper(); 
    Model user = mapper.readValue(MODEL_BINDING, Model.class);//readValue到一个实体类中. 
    System.out.println(user.getName()); 
    System.out.println(user.getType()); 
  } 
Model类:

private static class Model{ 
    private String name; 
    private int type; 
      
    public String getName() { 
      return name; 
    } 
    public void setName(String name) { 
      this.name = name; 
    } 
    public int getType() { 
      return type; 
    } 
    public void setType(int type) { 
      this.type = type; 
    } 
  } 
(2)Raw Data Binding:

/** 
  Concrete Java types that Jackson will use for simple data binding are: 
  JSON Type    Java Type 
  object     LinkedHashMap<String,Object> 
  array      ArrayList<Object> 
  string     String 
  number(no fraction) Integer, Long or BigInteger (smallest applicable) 
  number(fraction)  Double(configurable to use BigDecimal) 
  true|false   Boolean 
  null      null 
  */
  public void rawDataBinding() throws Exception{ 
    ObjectMapper mapper = new ObjectMapper(); 
    HashMap map = mapper.readValue(MODEL_BINDING,HashMap.class);//readValue到一个原始数据类型. 
    System.out.println(map.get(“name”)); 
    System.out.println(map.get(“type”)); 
  } 
 (3)generic Data Binding:

private static final String GENERIC_BINDING = “{\”key1\”:{\”name\”:\”name2\”,\”type\”:2},\”key2\”:{\”name\”:\”name3\”,\”type\”:3}}”; 
  public void genericDataBinding() throws Exception{ 
    ObjectMapper mapper = new ObjectMapper(); 
    HashMap<String,Model> modelMap = mapper.readValue(GENERIC_BINDING,new TypeReference<HashMap<String,Model>>(){});//readValue到一个范型数据中. 
    Model model = modelMap.get(“key2”); 
    System.out.println(model.getName()); 
    System.out.println(model.getType()); 
  } 
2、Tree Model:最灵活。

private static final String TREE_MODEL_BINDING = “{\”treekey1\”:\”treevalue1\”,\”treekey2\”:\”treevalue2\”,\”children\”:[{\”childkey1\”:\”childkey1\”}]}”; 
  public void treeModelBinding() throws Exception{ 
    ObjectMapper mapper = new ObjectMapper(); 
    JsonNode rootNode = mapper.readTree(TREE_MODEL_BINDING); 
    //path与get作用相同,但是当找不到该节点的时候,返回missing node而不是Null. 
    String treekey2value = rootNode.path(“treekey2”).getTextValue();// 
    System.out.println(“treekey2value:” + treekey2value); 
    JsonNode childrenNode = rootNode.path(“children”); 
    String childkey1Value = childrenNode.get(0).path(“childkey1”).getTextValue(); 
    System.out.println(“childkey1Value:”+childkey1Value); 
      
    //创建根节点 
    ObjectNode root = mapper.createObjectNode(); 
    //创建子节点1 
    ObjectNode node1 = mapper.createObjectNode(); 
    node1.put(“nodekey1”,1); 
    node1.put(“nodekey2”,2); 
    //绑定子节点1 
    root.put(“child”,node1); 
    //数组节点 
    ArrayNode arrayNode = mapper.createArrayNode(); 
    arrayNode.add(node1); 
    arrayNode.add(1); 
    //绑定数组节点 
    root.put(“arraynode”, arrayNode); 
    //JSON读到树节点 
    JsonNode valueToTreeNode = mapper.valueToTree(TREE_MODEL_BINDING); 
    //绑定JSON节点 
    root.put(“valuetotreenode”,valueToTreeNode); 
    //JSON绑定到JSON节点对象 
    JsonNode bindJsonNode = mapper.readValue(GENERIC_BINDING, JsonNode.class);//绑定JSON到JSON节点对象. 
    //绑定JSON节点 
    root.put(“bindJsonNode”,bindJsonNode); 
    System.out.println(mapper.writeValueAsString(root)); 
  } 
3、Streaming API:最佳性能。
 
对于性能要求高的程序,推荐使用流API,否则使用其他方法
不管是创建JsonGenerator还是JsonParser,都是使用JsonFactory。

packagecom.jingshou.jackson;
  
importjava.io.File;
importjava.io.IOException;
  
importcom.fasterxml.jackson.core.JsonEncoding;
importcom.fasterxml.jackson.core.JsonFactory;
importcom.fasterxml.jackson.core.JsonGenerator;
importcom.fasterxml.jackson.core.JsonParser;
importcom.fasterxml.jackson.core.JsonToken;
  
publicclassJacksonTest6 {
  
  publicstaticvoidmain(String[] args)throwsIOException {
    JsonFactory jfactory =newJsonFactory();
       
    /*** write to file ***/
    JsonGenerator jGenerator = jfactory.createGenerator(newFile(
        “c:\\user.json”), JsonEncoding.UTF8);
    jGenerator.writeStartObject();// {
     
    jGenerator.writeStringField(“name”,”mkyong”);// “name” : “mkyong”
    jGenerator.writeNumberField(“age”,29);// “age” : 29
     
    jGenerator.writeFieldName(“messages”);// “messages” :
    jGenerator.writeStartArray();// [
     
    jGenerator.writeString(“msg 1”);// “msg 1”
    jGenerator.writeString(“msg 2”);// “msg 2”
    jGenerator.writeString(“msg 3”);// “msg 3”
     
    jGenerator.writeEndArray();// ]
     
    jGenerator.writeEndObject();// }
    jGenerator.close();
      
    /*** read from file ***/
    JsonParser jParser = jfactory.createParser(newFile(“c:\\user.json”));
    // loop until token equal to “}”
    while(jParser.nextToken() != JsonToken.END_OBJECT) {
     
      String fieldname = jParser.getCurrentName();
      if(“name”.equals(fieldname)) {
     
       // current token is “name”,
       // move to next, which is “name”‘s value
       jParser.nextToken();
       System.out.println(jParser.getText());// display mkyong
     
      }
     
      if(“age”.equals(fieldname)) {
     
       // current token is “age”, 
       // move to next, which is “name”‘s value
       jParser.nextToken();
       System.out.println(jParser.getIntValue());// display 29
     
      }
     
      if(“messages”.equals(fieldname)) {
     
       jParser.nextToken();// current token is “[“, move next
     
       // messages is array, loop until token equal to “]”
       while(jParser.nextToken() != JsonToken.END_ARRAY) {
     
             // display msg1, msg2, msg3
         System.out.println(jParser.getText()); 
     
       }
     
      }
     
     }
     jParser.close();
  
  }
  
}

5月 072015
 

作者有是两篇文章,我合二为一了,写的挺好,分享下:

XML和Json作为最常用的两种网络传输格式而被广泛使用,XML在早期数据传输中作为首选,但是近年来Json以其轻量级和更容易编写和解析而越来越流行,Gson作为google的一个开源Json解析框架提供了稳定和快速解析的功能,可以读读它的源代码了解一番。

说到Gson,其实它无非就是做两个工作,序列化(Object—>JsonString)和反序列化(JsonString—>Object),后文所说的两个方向从Object到String和String到Object的两个方向。可想而知,对于序列化来说,是较为容易的工作,而对于反序列化即Json解析才是Gson的重头戏。既然是对Json字符串的解析,那么少不了对Json字符串中的结构进行抽象。

Json抽象类

JsonElement

这是Json中元素的基类,它只提供了若干个类型判断的接口,简单判断这个Json元素的类型。以下几个类型都是它的子类。

1、JsonObject

包含多个JsonElement的集合,它在Json中对应这种类型的数据:

{
    "count":100,
    "users":[],
    "paging":{
        "offset":0,
        "limit":10,
        "hasMore":true
    }
}

这个data是一个典型的JsonObject,它以{开头,其中包含了一些类似数值,数组,对象等其他JsonElement的内容。其实每一个Json字符串的根节点都是一个JsonObject或者JsonArray。

JsonObject提供了比较多的方法来得到Json中的信息,addProperty()函数可以在当前Json节点下新建子结点。

2、JsonArray

JsonArray也表示JsonElement的集合,注意:Json中的数组并不要求所有的数据类型都一样

[true,"hello"] //JsonArray,包括一个boolean和一个hello类型。

需要讨论的是JsonArray和JsonObject的区别是什么?从集合的角度来说,JsonObject中的JsonElement是无序的,而JsonArray中的集合元素是有序的,从直观感受来说,你可以通过下标来引用JsonArray中的元素,而JsonObject是通过键值对的方式来访问的,get(“name”)—>value。

3、JsonPrimitive

对应Json中的基本类型,比如boolean,int,当然提供了基本类型和包装类的自动替换。

"count":100

4、JsonNull

空,对应null

"person":null

以上就是Gson对应Json结构的封装。

注解-Annotations

Expose

在对象进行序列化和反序列化的过程中,我们可以通过注解来屏蔽某一些字段。这个注解默认有两个参数,serialize和deserialize都是默认true。如果设置为false,表示这个序列化(反)的过程中,这一个属性不需要进行处理。

通过Expose标注的属性在直接new Gson()的情况下不能生效,我们必须通过Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()来创建一个可以识别Expose注解的Gson。

小小的吐槽一把,这个地方的使用确实不太方便,举个栗子,一般来说,我们进行序列化的时候都是希望某一个属性不会序列化到Json字符里面(反之亦然),所以这里的一般思维是我要去处理这些特殊的属性。而如果你想通过Expose来去掉10个属性中的某一个,对不起,10个属性你都需要加上@Expose,然后对你想要处理的那个属性的Expose注解增加false参数,简直就是坑爹。。。。

//我想让Person类在序列化时,不去序列化password,是不是很坑爹?
class Person {
    @Expose
    private String userName;
    @Expose (serialize = false)
    private String passWord;
}

此外,和java本身的序列化一样,如果一个属性设置为transient或者static,那么两个序列化的两个方向上都会屏蔽掉这个属性,虽然比Expose简单,但是不够灵活。

SerializedName

这个注解使用较多,它会改变两个方向上这个属性的名称,在序列化是,JsonElement的键值会被替换成这个名字;解析时,Json中键值为这个名字的JsonElement会赋值给被注解的属性。

class Person {

@SerializedName(value = "A")
private int a = 1;
private int b = 2;
}

//{"A":1,"b":2}  这个Json字符串和前面的Person等价

它使用场景最多的地方就是比如后端返回的json中的名称和我们定义的model类名称不一样时使用。

Since 和 Until

我们可以对我们的Model类进行序列化(两个方向)的版本控制,Since和Until刚好是两个相反的意义。

例子:

class Person {
    @Since(value = 1.0)     //GsonBuilder指定版本要从1.0开始的Gson才能解析
    private int a = 1;

    @Until(value = 1.5)   //GsonBuilder指定版本到1.5的Gson都可以解析,超过了不能解析
    private int b = 2;
}

和Expose一样,要想Gson识别这两个注解,同样需要通过GsonBuilder.setVersion(double).create()来实现。

前文介绍了Gson对于Json规范中的类型进行了抽象定义,本文将来介绍stream包中的源码,这一块是Gson的核心代码之一。Gson中的JsonReader和JsonWriter抄自android.util包中的两个类,只是把其中的上下文栈由ArrayList换成了数组,提高了效率。这两个类为Json序列化和反序列化提供了基本接口。

反序列化—JsonReader

StringPool–(Flyweight Pattern)

在Stream包中提供了一种针对String的优化处理,可以减少内存中String对象的个数,途径就是通过StringPool来对字符串进行复用。需要注意的是,在Java中字符串对象是不能被修改的,这也是这个Pool能正常工作的重要原因。StringPool原理和HashMap一模一样,通过一个2的n次方(512)作为数组的长度,字符串的hascode计算方式和String中的一致。为了使得hashcode分布分散一点,同样使用hashmap的补偿hash函数对hashcode进行处理,这一块的具体优化处理可以关注以后的HashMap源码分析。

深度优先的解析策略

Gson解析Json字符采用的是深度优先策略,下面是一个简单解析的栗子:

[
<!--数组中每一个对象为一个message对象-->
{
  "id": 912345678901,
  "text": "How do I read a JSON stream in Java?",
  <!--geo是一个double类型的数组-->
  "geo": null,
  <!--user对象-->
  "user": {
    "name": "json_newb",
    "followers_count": 41
   }
},
{
  "id": 912345678902,
  "text": "@json_newb just use JsonReader!",
  "geo": [50.454722, -104.606667],
  "user": {
    "name": "jesse",
    "followers_count": 2
  }
}
]

深度解析的逻辑如下,先找到这个json字符串的开始位置,本例中是[,此时我们知道这个json字符串是一个数组,下一步从第一个元素开始解析(一层循环)。读取下一个字符,{,可以知道第一个元素为对象。对于{开始的JsonObject,我们知道它其中的内容肯定是键值对的样式,那我可以读取下一个字符,而且它必须为”(属性名),然后去读取属性值。ok,我们可以看到这是一种非常正常的解析逻辑,其实Gson的解析就是这么做的。

数据抽象

为了进行上面的解析过程,Gson中定义了两种数据类型:

JsonToken(解析类型)

由于Json定义规范的原因,我们在解析过程中只要解析到一个结构的第一个元素再联系上下文就可以推断这个结构的类型。JsonToken用来描述Json中的'{‘,’}’,'[‘,’]’,'”‘,NULL,number,boolean,属性名和属性字符串值,其实JsonToken的基本上等于每一个结构的开头,其中”既可以表示一个属性名也可以表示一个属性就是一个String,这需要按照上下文来确定。 在JsonReader定义了一个属性token,用来存储Json串解析位置的下一个JsonToken。

JsonScope(解析上下文)

用来描述当前正在解析的位置所处的一个状态,为解析提供一个上下文做推断。比如,目前我们解析到了一个{字符,那么下一个字符串必须是空或者’“‘,如此我们可以推断这个”肯定是一个JsonToken.Name(属性名)。此外,JsonScope还可以描述一个JsonArray或者JsonObject目前解析到的位置是否是第一个元素。JsonScope被存放在一个栈中,而且是成对出现。

private JsonScope[] stack = new JsonScope[32];
 {
   push(JsonScope.EMPTY_DOCUMENT);  //静态初始化时push空Json文件到栈顶
 }
 需要注意的是,这里这个栈之只实现了一个push操作,并提供了动态增长,出栈操作只需要简单的简单的stacksize--

JsonReader工作流

JsonReader的构造函数接受一个Reader的参数,它是一个Json串的InputStream包装出来的Reader。

对于这个上文Json文档的解析,JsonReader的理逻辑如下:

List<Message> result = new ArrayList<Message>();
jsonReader.beginArray();   //找到数组
while(jsonReader.hasnext) {
    Message message = new Message();
    jsonReader.beginObject();   //开始数组中的对象
    while(jsonReader.hasnext) {
        String name = jsonReader.nextName();
        if(name.equals("id")) {
            message.setId(jsonReader.nextString());
        }else if(name.equals("text")) {
            message.setText(jsonReader.nextString());
        }else if(name.equals("geo")&&jsonReader.peek()!=NULL) {
             List<Double> list = new ArrayList<Double>();
             jsonReader.beginArray();
             while(jsonReader.hasnext()) {
                list.add(jsonReader.nextDouble());
             }
             JsonReader.endArray();
        }
        jsonReader.beginObject();
       .....开始解析User对象......
    }
    result.add(message);
}

一些函数的解释

begin和 end系列函数

其实这两个函数只是从业务的角度出发,封装了一下,实际两个函数都是调用except(JsonToken),作用是寻找json字符串中的下一个JsonToken。以beginObject()函数来说,它直接调用except(JsonToken.BEGIN_OBJECT)。

private void expect(JsonToken expected) throws IOException {
peek();         //找到下一个JsonToken,赋值给JsonReader的token属性
if (token != expected) {  //判断下一个Token是否等于指定的Token类型
  throw new IllegalStateException("Expected " + expected + " but was " +        peek() + " at line " + getLineNumber() + " column " +       getColumnNumber());
}
advance();  //下一步操作
}

peek函数

peek函数是JsonReader中最重要的函数,它的主要作用是进行上下文的判断,判断下一个JsonToken的读取方法。

下面看一下peek()函数的实现:

public JsonToken peek() throws IOException {
//之前解析出来的token还没有被消费,直接取回
if (token != null) {
  return token;
}
//查看栈顶的JsonScope,这里可以看到JsonScope就是一个上下文的角色
switch (stack[stackSize - 1]) {
case EMPTY_DOCUMENT:    //json文档的开始
  if (lenient) {    //json防止攻击引入的前导,防止跨域攻击
    consumeNonExecutePrefix();
  }
  stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;   //注意这里不是push而是直接替换栈顶元素
  JsonToken firstToken = nextValue();   //返回流中的下一个开始JsonToken,在上个例子中就是'[',也就是这里返回的是一个JsonToken.BEGIN_ARRAY,这个值还会被保存在token属性中
  if (!lenient && token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {   //一个json文档开始无非就是一个对象或者数组
    throw new IOException("Expected JSON document to start with '[' or '{' but was " + token
        + " at line " + getLineNumber() + " column " + getColumnNumber());
  }
  return firstToken;
case EMPTY_ARRAY:
    //目前处于一个数组中,但是还没有开始读取任何数据
  return nextInArray(true);
case NONEMPTY_ARRAY:
  return nextInArray(false);
case EMPTY_OBJECT: //目前处于一个JsonObject中但是还没有读取任何数据
  return nextInObject(true);
case DANGLING_NAME: //目前处于一个属性名的位置
  return objectValue();
case NONEMPTY_OBJECT:
  return nextInObject(false);
case NONEMPTY_DOCUMENT:
    //当前处于一个文档的解析上下文中,可以理解为根元素下一级的环境
  int c = nextNonWhitespace(false);
  if (c == -1) {
    return JsonToken.END_DOCUMENT;
  }
  pos--;
  if (!lenient) {
    throw syntaxError("Expected EOF");
  }
  //找下一个JsonToken
  return nextValue();
case CLOSED:
  throw new IllegalStateException("JsonReader is closed");
default:
  throw new AssertionError();
}
}

nextIn**()

提供了数组、JsonObject的解析方法。

nextValue()

找下一个JsonToken

private JsonToken nextValue() throws IOException {
int c = nextNonWhitespace(true);    //找到当前解析位置之后的第一个非空白字符
switch (c) {
case '{':
  push(JsonScope.EMPTY_OBJECT);   //进入一个JsonObject
  return token = JsonToken.BEGIN_OBJECT;

case '[':
  push(JsonScope.EMPTY_ARRAY);   //进入一个数组
  return token = JsonToken.BEGIN_ARRAY;
  //以上两种情况说明是一个新的结构的开始,所以需要pushJsonScope到栈中
case ''':
  checkLenient(); // fall-through
case '"':
  //代表一个Json字符串值
  value = nextString((char) c);  //找到两个引号之间的String值
  return token = JsonToken.STRING;

default:
  pos--;
  return readLiteral();
}
}

JsonReader总结

JsonReader可以看作一个最基本的Json解析的接口,JsonReader中通过一个上下文来保存当前解析的环境,通过next**系列函数来获取下一个JsonToken。

序列化–JsonWriter

理解了JsonReader的源码之后,再来看Writer就相对来说简单多了。现在我们有一个数组,List,它的值就如上面的例子,那如何序列化呢?

public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
      JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
      writer.setIndent("t");           //设置每一行的缩进
      writeMessagesArray(writer, messages);
      writer.close();
    }

    public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
      writer.beginArray();   //[
      for (Message message : messages) {
        writeMessage(writer, message);
      }
      writer.endArray();
    }

    public void writeMessage(JsonWriter writer, Message message) throws IOException {
      writer.beginObject(); //{
      writer.name("id").value(message.getId()); // "id":23435356
      writer.name("text").value(message.getText()); //"text":"dflsfldsfljd"
      if (message.getGeo() != null) {
        writer.name("geo");
        writeDoublesArray(writer, message.getGeo());
      } else {
        writer.name("geo").nullValue();
      }
      writer.name("user");
      writeUser(writer, message.getUser());
      writer.endObject();
    }

    public void writeUser(JsonWriter writer, User user) throws IOException {
      writer.beginObject();
      writer.name("name").value(user.getName());
      writer.name("followers_count").value(user.getFollowersCount());
      writer.endObject();
    }

    public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
      writer.beginArray();
      for (Double value : doubles) {
        writer.value(value);
      }
      writer.endArray();
    }

JsonWriter中的函数

事实上,可以看到JsonWriter的工作和JsonReader刚好相反,两个类的对json字符串的处理方式也基本相同。下面说一些业务流程中较为重要的方法。

beginArray and beginObject

开始向流中写入一个数组或者JsonObject的开始,注意,这里不仅仅是写入一个[或者一个{这么简单,首先会调用writeDeferredName方法,它的主要功能是如果这个开始的JsonElement是JsonObject中的一个属性,那么它的前面肯定有一个和上一个元素的分割符号和一个名字。

endArray and endObject

结束一个数组和对象。

转自:http://chuyun923.github.io/blog/2015/01/10/gsonyuan-ma-zhi-streamchu-li/

http://chuyun923.github.io/blog/2015/01/06/gsonyuan-ma-fen-xi/

4月 232015
 

问题:

使用gson转json时出现下面错误:

com.google.gson.JsonParseException: Type information is unavailable, and the target object is not a primitive:xxxxx

分析:

为了转换方便,一般都会使用key:value方式组成json,有时会使用到下面这样稍微复杂些的json格式:

{

    “name”:”tom”,

    “age”:”18″,

    “scores”:

            {

                “sc1“:”100”,

                “sc2″:”90”

            }

}


当使用:

Gson gson = new GsonBuilder();

Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String,Object> arg = (Map<String, Object>) gson.fromJson(body, type);

时出现上述错误。 


解决方式:

 升级Gson包, 新Gson包有LinkedTreeMap可解决上述问题。 就是不使用LinkedTreeMap,直接object都可,搞定!


1月 212015
 

网上找到的关于Gson各特殊类型转换的代码实例,非常好,分享下。

转自:http://blog.csdn.net/lk_blog/article/details/7685190

Json转换利器Gson之实例一-简单对象转化和带泛型的List转化

Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。可以将一个 JSON 字符串转成一个 Java 对象,或者反过来。

jar和源码下载地址: http://code.google.com/p/google-gson/downloads/list


实体类:

[java] view plaincopy

  1.   
[java] view plaincopy

  1. public class Student {  
  2.     private int id;  
  3.     private String name;  
  4.     private Date birthDay;  
  5.   
  6.     public int getId() {  
  7.         return id;  
  8.     }  
  9.   
  10.     public void setId(int id) {  
  11.         this.id = id;  
  12.     }  
  13.   
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.   
  18.     public void setName(String name) {  
  19.         this.name = name;  
  20.     }  
  21.   
  22.     public Date getBirthDay() {  
  23.         return birthDay;  
  24.     }  
  25.   
  26.     public void setBirthDay(Date birthDay) {  
  27.         this.birthDay = birthDay;  
  28.     }  
  29.   
  30.     @Override  
  31.     public String toString() {  
  32.         return “Student [birthDay=” + birthDay + “, id=” + id + “, name=”  
  33.                 + name + “]”;  
  34.     }  
  35.   
  36. }  

测试类:

[html] view plaincopy

  1. import java.util.ArrayList;  
  2. import java.util.Date;  
  3. import java.util.List;  
  4.   
  5. import com.google.gson.Gson;  
  6. import com.google.gson.reflect.TypeToken;  
  7.   
  8. public class GsonTest1 {  
  9.   
  10.     public static void main(String[] args) {  
  11.         Gson gson = new Gson();  
  12.   
  13.         Student student1 = new Student();  
  14.         student1.setId(1);  
  15.         student1.setName(“李坤”);  
  16.         student1.setBirthDay(new Date());  
  17.   
  18.         // //////////////////////////////////////////////////////////  
  19.         System.out.println(“———-简单对象之间的转化————-“);  
  20.         // 简单的bean转为json  
  21.         String s1 = gson.toJson(student1);  
  22.         System.out.println(“简单Bean转化为Json===” + s1);  
  23.   
  24.         // json转为简单Bean  
  25.         Student student = gson.fromJson(s1, Student.class);  
  26.         System.out.println(“Json转为简单Bean===” + student);  
  27.         // 结果:  
  28.         // 简单Bean转化为Json==={“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 8:27:52 AM”}  
  29.         // Json转为简单Bean===Student [birthDay=Fri Jun 22 08:27:52 CST 2012, id=1,  
  30.         // name=李坤]  
  31.         // //////////////////////////////////////////////////////////  
  32.   
  33.         Student student2 = new Student();  
  34.         student2.setId(2);  
  35.         student2.setName(“曹贵生”);  
  36.         student2.setBirthDay(new Date());  
  37.   
  38.         Student student3 = new Student();  
  39.         student3.setId(3);  
  40.         student3.setName(“柳波”);  
  41.         student3.setBirthDay(new Date());  
  42.   
  43.         List<Student> list = new ArrayList<Student>();  
  44.         list.add(student1);  
  45.         list.add(student2);  
  46.         list.add(student3);  
  47.   
  48.         System.out.println(“———-带泛型的List之间的转化————-“);  
  49.         // 带泛型的list转化为json  
  50.         String s2 = gson.toJson(list);  
  51.         System.out.println(“带泛型的list转化为json==” + s2);  
  52.   
  53.         // json转为带泛型的list  
  54.         List<Student> retList = gson.fromJson(s2,  
  55.                 new TypeToken<List<Student>>() {  
  56.                 }.getType());  
  57.         for (Student stu : retList) {  
  58.             System.out.println(stu);  
  59.         }  
  60.   
  61.         // 结果:  
  62.         // 带泛型的list转化为json==[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 8:28:52 AM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 8:28:52 AM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 8:28:52 AM”}]  
  63.         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=1name=李坤]  
  64.         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=2name=曹贵生]  
  65.         // Student [birthDay=Fri Jun 22 08:28:52 CST 2012, id=3name=柳波]  
  66.   
  67.     }  
  68. }  

执行结果:

[plain] view plaincopy

  1. ———-简单对象之间的转化————-  
  2. 简单Bean转化为Json==={“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 9:10:31 PM”}  
  3. Json转为简单Bean===Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=1, name=李坤]  
  4. ———-带泛型的List之间的转化————-  
  5. 带泛型的list转化为json==[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 9:10:31 PM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 9:10:31 PM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 9:10:31 PM”}]  
  6. Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=1, name=李坤]  
  7. Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=2, name=曹贵生]  
  8. Student [birthDay=Fri Jun 22 21:10:31 CST 2012, id=3, name=柳波]  

Json转换利器Gson之实例二-Gson注解和GsonBuilder

有时候我们不需要把实体的所有属性都导出,只想把一部分属性导出为Json.

有时候我们的实体类会随着版本的升级而修改.

有时候我们想对输出的json默认排好格式.

… …

请看下面的例子吧:

实体类:

[java] view plaincopy

  1. import java.util.Date;  
  2.   
  3. import com.google.gson.annotations.Expose;  
  4. import com.google.gson.annotations.SerializedName;  
  5.   
  6. public class Student {  
  7.     private int id;  
  8.       
  9.     @Expose  
  10.     private String name;  
  11.       
  12.     @Expose  
  13.     @SerializedName(“bir”)  
  14.     private Date birthDay;  
  15.   
  16.     public int getId() {  
  17.         return id;  
  18.     }  
  19.   
  20.     public void setId(int id) {  
  21.         this.id = id;  
  22.     }  
  23.   
  24.     public String getName() {  
  25.         return name;  
  26.     }  
  27.   
  28.     public void setName(String name) {  
  29.         this.name = name;  
  30.     }  
  31.   
  32.     public Date getBirthDay() {  
  33.         return birthDay;  
  34.     }  
  35.   
  36.     public void setBirthDay(Date birthDay) {  
  37.         this.birthDay = birthDay;  
  38.     }  
  39.   
  40.     @Override  
  41.     public String toString() {  
  42.         return “Student [birthDay=” + birthDay + “, id=” + id + “, name=”  
  43.                 + name + “]”;  
  44.     }  
  45.   
  46. }  

测试类:

[java] view plaincopy

  1. import java.util.ArrayList;  
  2. import java.util.Date;  
  3. import java.util.List;  
  4.   
  5. import com.google.gson.FieldNamingPolicy;  
  6. import com.google.gson.Gson;  
  7. import com.google.gson.GsonBuilder;  
  8. import com.google.gson.reflect.TypeToken;  
  9.   
  10. public class GsonTest2 {  
  11.   
  12.     public static void main(String[] args) {  
  13.         //注意这里的Gson的构建方式为GsonBuilder,区别于test1中的Gson gson = new Gson();  
  14.         Gson gson = new GsonBuilder()  
  15.         .excludeFieldsWithoutExposeAnnotation() //不导出实体中没有用@Expose注解的属性  
  16.         .enableComplexMapKeySerialization() //支持Map的key为复杂对象的形式  
  17.         .serializeNulls().setDateFormat(“yyyy-MM-dd HH:mm:ss:SSS”)//时间转化为特定格式    
  18.         .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//会把字段首字母大写,注:对于实体上使用了@SerializedName注解的不会生效.  
  19.         .setPrettyPrinting() //对json结果格式化.  
  20.         .setVersion(1.0)    //有的字段不是一开始就有的,会随着版本的升级添加进来,那么在进行序列化和返序列化的时候就会根据版本号来选择是否要序列化.  
  21.                             //@Since(版本号)能完美地实现这个功能.还的字段可能,随着版本的升级而删除,那么  
  22.                             //@Until(版本号)也能实现这个功能,GsonBuilder.setVersion(double)方法需要调用.  
  23.         .create();  
  24.           
  25.           
  26.   
  27.         Student student1 = new Student();  
  28.         student1.setId(1);  
  29.         student1.setName(“李坤”);  
  30.         student1.setBirthDay(new Date());  
  31.   
  32.         // //////////////////////////////////////////////////////////  
  33.         System.out.println(“———-简单对象之间的转化————-“);  
  34.         // 简单的bean转为json  
  35.         String s1 = gson.toJson(student1);  
  36.         System.out.println(“简单Bean转化为Json===” + s1);  
  37.   
  38.         // json转为简单Bean  
  39.         Student student = gson.fromJson(s1, Student.class);  
  40.         System.out.println(“Json转为简单Bean===” + student);  
  41.         // //////////////////////////////////////////////////////////  
  42.   
  43.         Student student2 = new Student();  
  44.         student2.setId(2);  
  45.         student2.setName(“曹贵生”);  
  46.         student2.setBirthDay(new Date());  
  47.   
  48.         Student student3 = new Student();  
  49.         student3.setId(3);  
  50.         student3.setName(“柳波”);  
  51.         student3.setBirthDay(new Date());  
  52.   
  53.         List<Student> list = new ArrayList<Student>();  
  54.         list.add(student1);  
  55.         list.add(student2);  
  56.         list.add(student3);  
  57.   
  58.         System.out.println(“———-带泛型的List之间的转化————-“);  
  59.         // 带泛型的list转化为json  
  60.         String s2 = gson.toJson(list);  
  61.         System.out.println(“带泛型的list转化为json==” + s2);  
  62.   
  63.         // json转为带泛型的list  
  64.         List<Student> retList = gson.fromJson(s2,  
  65.                 new TypeToken<List<Student>>() {  
  66.                 }.getType());  
  67.         for (Student stu : retList) {  
  68.             System.out.println(stu);  
  69.         }  
  70.           
  71.     }  
  72. }  

输出结果:

[plain] view plaincopy

  1. ———-简单对象之间的转化————-  
  2. 简单Bean转化为Json==={  
  3.   “Name”: “李坤”,  
  4.   “bir”: “2012-06-22 21:26:40:592”  
  5. }  
  6. Json转为简单Bean===Student [birthDay=Fri Jun 22 21:26:40 CST 2012, id=0, name=李坤]  
  7. ———-带泛型的List之间的转化————-  
  8. 带泛型的list转化为json==[  
  9.   {  
  10.     “Name”: “李坤”,  
  11.     “bir”: “2012-06-22 21:26:40:592”  
  12.   },  
  13.   {  
  14.     “Name”: “曹贵生”,  
  15.     “bir”: “2012-06-22 21:26:40:625”  
  16.   },  
  17.   {  
  18.     “Name”: “柳波”,  
  19.     “bir”: “2012-06-22 21:26:40:625”  
  20.   }  
  21. ]  
  22. Student [birthDay=Fri Jun 22 21:26:40 CST 2012, id=0, name=李坤]  
  23. Student [birthDay=Fri Jun 22 21:26:40 CST 2012, id=0, name=曹贵生]  
  24. Student [birthDay=Fri Jun 22 21:26:40 CST 2012, id=0, name=柳波]  

 

Json转换利器Gson之实例三-Map处理

Map的存储结构式Key/Value形式,Key 和 Value可以是普通类型,也可以是自己写的JavaBean(本文),还可以是带有泛型的List(下一篇博客).本例中您要重点看如何将Json转回为普通JavaBean对象时TypeToken的定义.

实体类:

[java] view plaincopy

  1. public class Point {  
  2.     private int x;  
  3.     private int y;  
  4.   
  5.     public Point(int x, int y) {  
  6.         this.x = x;  
  7.         this.y = y;  
  8.     }  
  9.   
  10.     public int getX() {  
  11.         return x;  
  12.     }  
  13.   
  14.     public void setX(int x) {  
  15.         this.x = x;  
  16.     }  
  17.   
  18.     public int getY() {  
  19.         return y;  
  20.     }  
  21.   
  22.     public void setY(int y) {  
  23.         this.y = y;  
  24.     }  
  25.   
  26.     @Override  
  27.     public String toString() {  
  28.         return “Point [x=” + x + “, y=” + y + “]”;  
  29.     }  
  30.   
  31. }  

测试类:

[java] view plaincopy

  1. import java.util.LinkedHashMap;  
  2. import java.util.Map;  
  3.   
  4. import com.google.gson.Gson;  
  5. import com.google.gson.GsonBuilder;  
  6. import com.google.gson.reflect.TypeToken;  
  7.   
  8. public class GsonTest3 {  
  9.   
  10.     public static void main(String[] args) {  
  11.         Gson gson = new GsonBuilder().enableComplexMapKeySerialization()  
  12.                 .create();  
  13.   
  14.         Map<Point, String> map1 = new LinkedHashMap<Point, String>();// 使用LinkedHashMap将结果按先进先出顺序排列  
  15.         map1.put(new Point(56), “a”);  
  16.         map1.put(new Point(88), “b”);  
  17.         String s = gson.toJson(map1);  
  18.         System.out.println(s);// 结果:[[{“x”:5,”y”:6},”a”],[{“x”:8,”y”:8},”b”]]  
  19.   
  20.         Map<Point, String> retMap = gson.fromJson(s,  
  21.                 new TypeToken<Map<Point, String>>() {  
  22.                 }.getType());  
  23.         for (Point p : retMap.keySet()) {  
  24.             System.out.println(“key:” + p + ” values:” + retMap.get(p));  
  25.         }  
  26.         System.out.println(retMap);  
  27.   
  28.         System.out.println(“———————————-“);  
  29.         Map<String, Point> map2 = new LinkedHashMap<String, Point>();  
  30.         map2.put(“a”new Point(34));  
  31.         map2.put(“b”new Point(56));  
  32.         String s2 = gson.toJson(map2);  
  33.         System.out.println(s2);  
  34.   
  35.         Map<String, Point> retMap2 = gson.fromJson(s2,  
  36.                 new TypeToken<Map<String, Point>>() {  
  37.                 }.getType());  
  38.         for (String key : retMap2.keySet()) {  
  39.             System.out.println(“key:” + key + ” values:” + retMap2.get(key));  
  40.         }  
  41.   
  42.     }  
  43. }  

结果:

[plain] view plaincopy

  1. [[{“x”:5,”y”:6},”a”],[{“x”:8,”y”:8},”b”]]  
  2. key:Point [x=5, y=6] values:a  
  3. key:Point [x=8, y=8] values:b  
  4. {Point [x=5, y=6]=a, Point [x=8, y=8]=b}  
  5. ———————————-  
  6. {“a”:{“x”:3,”y”:4},”b”:{“x”:5,”y”:6}}  
  7. key:a values:Point [x=3, y=4]  
  8. key:b values:Point [x=5, y=6]  

Map的存储结构式Key/Value形式,Key 和 Value可以是普通类型,也可以是自己写的JavaBean,还可以是带有泛型的List(本文).本例中您要重点看如何将Json转回为带泛型的对象List,并且List中的泛型对象有多种实体.

实体类:

[java] view plaincopy

  1. import java.util.Date;  
  2.   
  3. public class Student {  
  4.     private int id;  
  5.     private String name;  
  6.     private Date birthDay;  
  7.   
  8.     public int getId() {  
  9.         return id;  
  10.     }  
  11.   
  12.     public void setId(int id) {  
  13.         this.id = id;  
  14.     }  
  15.   
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.   
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.   
  24.     public Date getBirthDay() {  
  25.         return birthDay;  
  26.     }  
  27.   
  28.     public void setBirthDay(Date birthDay) {  
  29.         this.birthDay = birthDay;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return “Student [birthDay=” + birthDay + “, id=” + id + “, name=”  
  35.                 + name + “]”;  
  36.     }  
  37.   
  38. }  

[java] view plaincopy

  1. public class Teacher {  
  2.     private int id;  
  3.   
  4.     private String name;  
  5.   
  6.     private String title;  
  7.   
  8.     public int getId() {  
  9.         return id;  
  10.     }  
  11.   
  12.     public void setId(int id) {  
  13.         this.id = id;  
  14.     }  
  15.   
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.   
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.   
  24.     public String getTitle() {  
  25.         return title;  
  26.     }  
  27.   
  28.     public void setTitle(String title) {  
  29.         this.title = title;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return “Teacher [id=” + id + “, name=” + name + “, title=” + title  
  35.                 + “]”;  
  36.     }  
  37.   
  38. }  

测试类:

[java] view plaincopy

  1. package com.tgb.lk.demo.gson.test4;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Date;  
  5. import java.util.LinkedHashMap;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8.   
  9. import com.google.gson.Gson;  
  10. import com.google.gson.reflect.TypeToken;  
  11.   
  12. public class GsonTest4 {  
  13.     public static void main(String[] args) {  
  14.         Student student1 = new Student();  
  15.         student1.setId(1);  
  16.         student1.setName(“李坤”);  
  17.         student1.setBirthDay(new Date());  
  18.   
  19.         Student student2 = new Student();  
  20.         student2.setId(2);  
  21.         student2.setName(“曹贵生”);  
  22.         student2.setBirthDay(new Date());  
  23.   
  24.         Student student3 = new Student();  
  25.         student3.setId(3);  
  26.         student3.setName(“柳波”);  
  27.         student3.setBirthDay(new Date());  
  28.   
  29.         List<Student> stulist = new ArrayList<Student>();  
  30.         stulist.add(student1);  
  31.         stulist.add(student2);  
  32.         stulist.add(student3);  
  33.   
  34.         Teacher teacher1 = new Teacher();  
  35.         teacher1.setId(1);  
  36.         teacher1.setName(“米老师”);  
  37.         teacher1.setTitle(“教授”);  
  38.   
  39.         Teacher teacher2 = new Teacher();  
  40.         teacher2.setId(2);  
  41.         teacher2.setName(“丁老师”);  
  42.         teacher2.setTitle(“讲师”);  
  43.         List<Teacher> teacherList = new ArrayList<Teacher>();  
  44.         teacherList.add(teacher1);  
  45.         teacherList.add(teacher2);  
  46.   
  47.         Map<String, Object> map = new LinkedHashMap<String, Object>();  
  48.         map.put(“students”, stulist);  
  49.         map.put(“teachers”, teacherList);  
  50.   
  51.         Gson gson = new Gson();  
  52.         String s = gson.toJson(map);  
  53.         System.out.println(s);  
  54.   
  55.         System.out.println(“———————————-“);  
  56.   
  57.         Map<String, Object> retMap = gson.fromJson(s,  
  58.                 new TypeToken<Map<String, List<Object>>>() {  
  59.                 }.getType());  
  60.   
  61.         for (String key : retMap.keySet()) {  
  62.             System.out.println(“key:” + key + ” values:” + retMap.get(key));  
  63.             if (key.equals(“students”)) {  
  64.                 List<Student> stuList = (List<Student>) retMap.get(key);  
  65.                 System.out.println(stuList);  
  66.             } else if (key.equals(“teachers”)) {  
  67.                 List<Teacher> tchrList = (List<Teacher>) retMap.get(key);  
  68.                 System.out.println(tchrList);  
  69.             }  
  70.         }  
  71.   
  72.     }  
  73. }  

输出结果:

[plain] view plaincopy

  1. {“students”:[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 9:48:19 PM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 9:48:19 PM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 9:48:19 PM”}],”teachers”:[{“id”:1,”name”:”米老师”,”title”:”教授”},{“id”:2,”name”:”丁老师”,”title”:”讲师”}]}  
  2. ———————————-  
  3. key:students values:[{id=1.0, name=李坤, birthDay=Jun 22, 2012 9:48:19 PM}, {id=2.0, name=曹贵生, birthDay=Jun 22, 2012 9:48:19 PM}, {id=3.0, name=柳波, birthDay=Jun 22, 2012 9:48:19 PM}]  
  4. [{id=1.0, name=李坤, birthDay=Jun 22, 2012 9:48:19 PM}, {id=2.0, name=曹贵生, birthDay=Jun 22, 2012 9:48:19 PM}, {id=3.0, name=柳波, birthDay=Jun 22, 2012 9:48:19 PM}]  
  5. key:teachers values:[{id=1.0, name=米老师, title=教授}, {id=2.0, name=丁老师, title=讲师}]  
  6. [{id=1.0, name=米老师, title=教授}, {id=2.0, name=丁老师, title=讲师}]  

 

Json转换利器Gson之实例-实际开发中的特殊需求处理

前面博客基本上可以满足我们处理的绝大多数需求,但有时项目中对json有特殊的格式规定.比如下面的json串解析:

[{“tableName”:”students”,”tableData”:[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 9:54:49 PM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 9:54:49 PM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 9:54:49 PM”}]},{“tableName”:”teachers”,”tableData”:[{“id”:1,”name”:”米老师”,”title”:”教授”},{“id”:2,”name”:”丁老师”,”title”:”讲师”}]}]

分析之后我们发现使用前面博客中用到的都不好处理上面的json串.请看本文是如何处理的吧:

实体类:

[java] view plaincopy

  1. import java.util.Date;  
  2.   
  3. public class Student {  
  4.     private int id;  
  5.     private String name;  
  6.     private Date birthDay;  
  7.   
  8.     public int getId() {  
  9.         return id;  
  10.     }  
  11.   
  12.     public void setId(int id) {  
  13.         this.id = id;  
  14.     }  
  15.   
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.   
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.   
  24.     public Date getBirthDay() {  
  25.         return birthDay;  
  26.     }  
  27.   
  28.     public void setBirthDay(Date birthDay) {  
  29.         this.birthDay = birthDay;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return “Student [birthDay=” + birthDay + “, id=” + id + “, name=”  
  35.                 + name + “]”;  
  36.     }  
  37.   
  38. }  

[java] view plaincopy

  1. public class Teacher {  
  2.     private int id;  
  3.   
  4.     private String name;  
  5.   
  6.     private String title;  
  7.   
  8.     public int getId() {  
  9.         return id;  
  10.     }  
  11.   
  12.     public void setId(int id) {  
  13.         this.id = id;  
  14.     }  
  15.   
  16.     public String getName() {  
  17.         return name;  
  18.     }  
  19.   
  20.     public void setName(String name) {  
  21.         this.name = name;  
  22.     }  
  23.   
  24.     public String getTitle() {  
  25.         return title;  
  26.     }  
  27.   
  28.     public void setTitle(String title) {  
  29.         this.title = title;  
  30.     }  
  31.   
  32.     @Override  
  33.     public String toString() {  
  34.         return “Teacher [id=” + id + “, name=” + name + “, title=” + title  
  35.                 + “]”;  
  36.     }  
  37.   
  38. }  

注意这里定义了一个TableData实体类:

[java] view plaincopy

  1. import java.util.List;  
  2.   
  3. public class TableData {  
  4.   
  5.     private String tableName;  
  6.   
  7.     private List tableData;  
  8.   
  9.     public String getTableName() {  
  10.         return tableName;  
  11.     }  
  12.   
  13.     public void setTableName(String tableName) {  
  14.         this.tableName = tableName;  
  15.     }  
  16.   
  17.     public List getTableData() {  
  18.         return tableData;  
  19.     }  
  20.   
  21.     public void setTableData(List tableData) {  
  22.         this.tableData = tableData;  
  23.     }  
  24. }  

测试类:

(仔细看将json转回为对象的实现,这里经过两次转化,第一次转回的结果是map不是我们所期望的对象,对map再次转为json后再转为对象,我引用的是Gson2.1的jar处理正常,好像使用Gson1.6的jar会报错,所以建议用最新版本)

[java] view plaincopy

  1. import java.util.ArrayList;  
  2. import java.util.Date;  
  3. import java.util.List;  
  4.   
  5. import com.google.gson.Gson;  
  6. import com.google.gson.reflect.TypeToken;  
  7.   
  8. public class GsonTest5 {  
  9.   
  10.     /** 
  11.      * @param args 
  12.      */  
  13.     public static void main(String[] args) {  
  14.         // 对象转为Json–>start  
  15.         Student student1 = new Student();  
  16.         student1.setId(1);  
  17.         student1.setName(“李坤”);  
  18.         student1.setBirthDay(new Date());  
  19.   
  20.         Student student2 = new Student();  
  21.         student2.setId(2);  
  22.         student2.setName(“曹贵生”);  
  23.         student2.setBirthDay(new Date());  
  24.   
  25.         Student student3 = new Student();  
  26.         student3.setId(3);  
  27.         student3.setName(“柳波”);  
  28.         student3.setBirthDay(new Date());  
  29.   
  30.         List<Student> stulist = new ArrayList<Student>();  
  31.         stulist.add(student1);  
  32.         stulist.add(student2);  
  33.         stulist.add(student3);  
  34.   
  35.         Teacher teacher1 = new Teacher();  
  36.         teacher1.setId(1);  
  37.         teacher1.setName(“米老师”);  
  38.         teacher1.setTitle(“教授”);  
  39.   
  40.         Teacher teacher2 = new Teacher();  
  41.         teacher2.setId(2);  
  42.         teacher2.setName(“丁老师”);  
  43.         teacher2.setTitle(“讲师”);  
  44.         List<Teacher> teacherList = new ArrayList<Teacher>();  
  45.         teacherList.add(teacher1);  
  46.         teacherList.add(teacher2);  
  47.   
  48.         TableData td1 = new TableData();  
  49.         td1.setTableName(“students”);  
  50.         td1.setTableData(stulist);  
  51.   
  52.         TableData td2 = new TableData();  
  53.         td2.setTableName(“teachers”);  
  54.         td2.setTableData(teacherList);  
  55.   
  56.         List<TableData> tdList = new ArrayList<TableData>();  
  57.         tdList.add(td1);  
  58.         tdList.add(td2);  
  59.         Gson gson = new Gson();  
  60.         String s = gson.toJson(tdList);  
  61.   
  62.         System.out.println(s);  
  63.         // 结果:[{“tableName”:”students”,”tableData”:[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 10:44:16 AM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 10:44:16 AM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 10:44:16 AM”}]},{“tableName”:”teachers”,”tableData”:[{“id”:1,”name”:”米老师”,”title”:”教授”},{“id”:2,”name”:”丁老师”,”title”:”讲师”}]}]  
  64.         // 对象转为Json–>end  
  65.   
  66.         // /////////////////////////////////////////////////////////////////////  
  67.   
  68.         // 将json转为数据–>start  
  69.         List<TableData> tableDatas2 = gson.fromJson(s,  
  70.                 new TypeToken<List<TableData>>() {  
  71.                 }.getType());  
  72.         for (int i = 0; i < tableDatas2.size(); i++) {  
  73.             TableData entityData = tableDatas2.get(i);  
  74.             String tableName = entityData.getTableName();  
  75.             List tableData = entityData.getTableData();  
  76.             String s2 = gson.toJson(tableData);  
  77.             // System.out.println(s2);  
  78.             // System.out.println(entityData.getData());  
  79.             if (tableName.equals(“students”)) {  
  80.                 System.out.println(“students”);  
  81.                 List<Student> retStuList = gson.fromJson(s2,  
  82.                         new TypeToken<List<Student>>() {  
  83.                         }.getType());  
  84.                 for (int j = 0; j < retStuList.size(); j++) {  
  85.                     System.out.println(retStuList.get(j));  
  86.                 }  
  87.   
  88.             } else if (tableName.equals(“teachers”)) {  
  89.                 System.out.println(“teachers”);  
  90.                 List<Teacher> retTchrList = gson.fromJson(s2,  
  91.                         new TypeToken<List<Teacher>>() {  
  92.                         }.getType());  
  93.                 for (int j = 0; j < retTchrList.size(); j++) {  
  94.                     System.out.println(retTchrList.get(j));  
  95.                 }  
  96.             }  
  97.         }  
  98.   
  99.         // Json转为对象–>end  
  100.     }  
  101. }  

输出结果:

[plain] view plaincopy

  1. [{“tableName”:”students”,”tableData”:[{“id”:1,”name”:”李坤”,”birthDay”:”Jun 22, 2012 10:04:12 PM”},{“id”:2,”name”:”曹贵生”,”birthDay”:”Jun 22, 2012 10:04:12 PM”},{“id”:3,”name”:”柳波”,”birthDay”:”Jun 22, 2012 10:04:12 PM”}]},{“tableName”:”teachers”,”tableData”:[{“id”:1,”name”:”米老师”,”title”:”教授”},{“id”:2,”name”:”丁老师”,”title”:”讲师”}]}]  
  2. students  
  3. Student [birthDay=Fri Jun 22 22:04:12 CST 2012, id=1, name=李坤]  
  4. Student [birthDay=Fri Jun 22 22:04:12 CST 2012, id=2, name=曹贵生]  
  5. Student [birthDay=Fri Jun 22 22:04:12 CST 2012, id=3, name=柳波]  
  6. teachers  
  7. Teacher [id=1, name=米老师, title=教授]  
  8. Teacher [id=2, name=丁老师, title=讲师]  

Json转换利器Gson之实例六-注册TypeAdapter及处理Enum类型

枚举类型给我们的程序带来了好处,如何用Gson来实现与Json的互转呢?请看本文.

本文重点掌握如何自己写一个TypeAdapter及注册TypeAdapter和处理Enum类型.

实体类:

[java] view plaincopy

  1. public enum PackageState {  
  2.     PLAY, UPDATE, UPDATING, DOWNLOAD, DOWNLOADING,  
  3. }  

[java] view plaincopy

  1. public class PackageItem {  
  2.     private String name;  
  3.     private PackageState state;  
  4.     private String size;  
  5.   
  6.     public String getName() {  
  7.         return name;  
  8.     }  
  9.   
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13.   
  14.     public PackageState getState() {  
  15.         return state;  
  16.     }  
  17.   
  18.     public void setState(PackageState state) {  
  19.         this.state = state;  
  20.     }  
  21.   
  22.     public String getSize() {  
  23.         return size;  
  24.     }  
  25.   
  26.     public void setSize(String size) {  
  27.         this.size = size;  
  28.     }  
  29.   
  30.     @Override  
  31.     public String toString() {  
  32.         return “PackageItem [name=” + name + “, size=” + size + “, state=”  
  33.                 + state + “]”;  
  34.     }  
  35. }  

自己写一个转换器实现JsonSerializer<T>接口和jsonDeserializer<T>接口:

[java] view plaincopy

  1. mport java.lang.reflect.Type;  
  2.   
  3. import com.google.gson.JsonDeserializationContext;  
  4. import com.google.gson.JsonDeserializer;  
  5. import com.google.gson.JsonElement;  
  6. import com.google.gson.JsonParseException;  
  7. import com.google.gson.JsonPrimitive;  
  8. import com.google.gson.JsonSerializationContext;  
  9. import com.google.gson.JsonSerializer;  
  10.   
  11. public class EnumSerializer implements JsonSerializer<PackageState>,  
  12.         JsonDeserializer<PackageState> {  
  13.   
  14.     // 对象转为Json时调用,实现JsonSerializer<PackageState>接口  
  15.     @Override  
  16.     public JsonElement serialize(PackageState state, Type arg1,  
  17.             JsonSerializationContext arg2) {  
  18.         return new JsonPrimitive(state.ordinal());  
  19.     }  
  20.   
  21.     // json转为对象时调用,实现JsonDeserializer<PackageState>接口  
  22.     @Override  
  23.     public PackageState deserialize(JsonElement json, Type typeOfT,  
  24.             JsonDeserializationContext context) throws JsonParseException {  
  25.         if (json.getAsInt() < PackageState.values().length)  
  26.             return PackageState.values()[json.getAsInt()];  
  27.         return null;  
  28.     }  
  29.   
  30. }  

测试类:

[java] view plaincopy

  1. import com.google.gson.Gson;  
  2. import com.google.gson.GsonBuilder;  
  3.   
  4. public class GsonTest6 {  
  5.   
  6.     public static void main(String[] args) {  
  7.         GsonBuilder gsonBuilder = new GsonBuilder();  
  8.         gsonBuilder.registerTypeAdapter(PackageState.class,  
  9.                 new EnumSerializer());  
  10.         Gson gson = gsonBuilder.create();  
  11.         PackageItem item = new PackageItem();  
  12.         item.setName(“item_name”);  
  13.         item.setSize(“500M”);  
  14.         item.setState(PackageState.UPDATING);// 这个 state是枚举值  
  15.   
  16.         String s = gson.toJson(item);  
  17.         System.out.println(s);  
  18.   
  19.         System.out.println(“——————————–“);  
  20.   
  21.         PackageItem retItem = gson.fromJson(s, PackageItem.class);  
  22.         System.out.println(retItem);  
  23.     }  
  24. }  


输出结果(结果中已经将state的对应枚举类型转为了int类型):

[plain] view plaincopy

  1. {“name”:”item_name”,”state”:2,”size”:”500M”}  
  2. ——————————–  
  3. PackageItem [name=item_name, size=500M, state=UPDATING]  

9月 282014
 

看到的一篇关于json和jsonp区别的文章,非常好,强烈推荐:

前言:

 

由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现。

当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的Socket通讯和本地数据库功能,又或者通过HTML5的WebSocket也可以实现与服务器的通讯和服务端推功能,但这两种方式都有其局限性,前者需要PhoneGap支持,后者要求用户设备必须支持WebSocket,因此都不能算是ST2的原生解决方案,原生的只有AJAX。

 

说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域可以通过服务器端代理来解决。

但到目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。而这就是本文将要讲述的内容。

 

JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。

 

既然随便聊聊,那我们就不再采用教条的方式来讲述,而是把关注重心放在帮助开发人员理解是否应当选择使用以及如何使用上。

小小的广告一下,该篇文章是在自己群里与Sencha Touch 2的开发者们一起探讨ST2数据交互模型时有感而发写出来的因此如果您对Mobile Web App开发有兴趣的话,欢迎加入Sencha Touch 交流 QQ 群 213119459 

 

 什么是JSON?

 

前面简单说了一下,JSON是一种基于文本的数据交换方式,或者叫做数据描述格式,你是否该选用他首先肯定要关注它所拥有的优点。

 

JSON的优点:

1、基于纯文本,跨平台传递极其简单;

2、Javascript原生支持,后台语言几乎全部支持;

3、轻量级数据格式,占用字符数量极少,特别适合互联网传递;

4、可读性较强,虽然比不上XML那么一目了然,但在合理的依次缩进之后还是很容易识别的;

5、容易编写和解析,当然前提是你要知道数据结构;

JSON的缺点当然也有,但在作者看来实在是无关紧要的东西,所以不再单独说明。

 

JSON的格式或者叫规则:

JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。

1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号””是定义符。

2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。

3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。

4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号””,以便于不同语言的解析。

5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。

 

JSON实例:

复制代码
// 描述一个人 var person = { "Name": "Bob", "Age": 32, "Company": "IBM", "Engineer": true } // 获取这个人的信息 var personAge = person.Age; // 描述几个人 var members = [
    { "Name": "Bob", "Age": 32, "Company": "IBM", "Engineer": true },
    { "Name": "John", "Age": 20, "Company": "Oracle", "Engineer": false },
    { "Name": "Henry", "Age": 45, "Company": "Microsoft", "Engineer": false }
] // 读取其中John的公司名称 var johnsCompany = members[1].Company; // 描述一次会议 var conference = { "Conference": "Future Marketing", "Date": "2012-6-1", "Address": "Beijing", "Members":
    [
        { "Name": "Bob", "Age": 32, "Company": "IBM", "Engineer": true },
        { "Name": "John", "Age": 20, "Company": "Oracle", "Engineer": false },
        { "Name": "Henry", "Age": 45, "Company": "Microsoft", "Engineer": false }
    ]
} // 读取参会者Henry是否工程师 var henryIsAnEngineer = conference.Members[2].Engineer;
复制代码

 

关于JSON,就说这么多,更多细节请在开发过程中查阅资料深入学习。

 

 什么是JSONP?

 

先说说JSONP是怎么产生的:

其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。

JSONP的客户端具体实现:

不管jQuery也好,extjs也罢,又或者是其他支持jsonp的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下jsonp在客户端的实现:

 

1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。

远程服务器remoteserver.com根目录下有个remote.js文件代码如下:

alert('我是远程文件');

本地服务器localserver.com下有个jsonp.html页面代码如下:

复制代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript" src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
复制代码

毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。

 

2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

jsonp.html页面代码如下:

复制代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript">  var localHandler = function(data){
        alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
    }; </script> <script type="text/javascript" src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
复制代码

remote.js文件代码如下:

localHandler({"result":"我是远程js带来的数据"});

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。

 

3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

看jsonp.html页面的代码:

复制代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> // 得到航班信息查询结果后的回调函数 var flightHandler = function(data){
        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
    }; // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码) var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"; // 创建script标签,设置其属性 var script = document.createElement('script');
    script.setAttribute('src', url); // 把script标签加入head,此时调用开始  document.getElementsByTagName('head')[0].appendChild(script); </script> </head> <body> </body> </html>
复制代码

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。

我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。

OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flightHandler({ "code": "CA1998", "price": 1780, "tickets": 5 });

我们看到,传递给flightHandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

 

4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

什么?你用的是jQuery,想知道jQuery如何实现jsonp调用?好吧,那我就好人做到底,再给你一段jQuery使用jsonp的代码(我们依然沿用上面那个航班信息查询的例子,假定返回jsonp结果不变):

复制代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Untitled Page</title> <script type="text/javascript" src=jquery.min.js"></script> <script type="text/javascript"> jQuery(document).ready(function(){
        $.ajax({
             type: "get",
             async: false,
             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
             dataType: "jsonp",
             jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)  jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据  success: function(json){
                 alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。'); },
             error: function(){
                 alert('fail');
             }
         });  }); </script> </head> <body> </body> </html>
复制代码

是不是有点奇怪?为什么我这次没有写flightHandler这个函数呢?而且竟然也运行成功了!哈哈,这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时(还是忍不住吐槽,虽然jquery也把jsonp归入了ajax,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供success属性方法来调用,是不是很爽呀?

这里针对ajax与jsonp的异同再做一些补充说明:

1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;

2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本

3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。

4、还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。

总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变着一点!

转自: http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html