JSON简介

JSON简介

JSON 是一种与开发语言无关的、轻量级的数据格式 - JavaScript Object Notation

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。

优点:易于人的阅读和编写,易于程序的解析和生产
基本格式:{key:value}

标准的json数据表示
数据结构:Object、Array
基本类型:String、number、true、false、null

因此如果需要表示时间类型的时候需要使用约定格式的字符串如"YYYY-MM-DD"  
然后在后台对数据进行解析

数据结构——object
使用花括号{}包含的键值对结构,key必须是String类型,value为任何基本类型或数据结构
数据结构——Array
使用中括号[]来起始,并用逗号来分割元素

下面这个网站可以帮助你解析json字符串,并生成文件:
JSON Editor Online

{
  "array": [
    1,
    2,
    3
  ],
  "boolean": true,
  "null": null,
  "number": 123,
  "object": {
    "a": "b",
    "c": "d",
    "e": "f"
  },
  "string": "Hello World"
}
注意,JSON文件里不允许注释

具体资料可以查阅-->JSON中文官方网站
这里还有所有语言的示例代码以及相关工具等等内容,十分详尽,如

Java:
    JSON-java.
    JSONUtil.
    jsonp.
    Json-lib.
    Stringtree.
    SOJO.
    json-taglib.
    Flexjson.
    JON tools.
    Argo.
    jsonij.
    fastjson.
    mjson.
    jjson.
    json-simple.
    json-io.
    JsonMarshaller.
    google-gson.
    Json-smart.
    FOSS Nova JSON.
    Corn CONVERTER.
    Apache johnzon.
    Genson.
    JSONUtil.
    cookjson.

JavaScript:
    JSON.
    json2.js.
    clarinet.
    Oboe.js.

利用org.Json构建json数据

利用org.Json在Java中使用JSONObject对象

Json是Android SDK的官方库
适合移动端开发

例子 JSON和对象的转换,在pom.xml中添加依赖包

<!--JAVA ASON--> 
<dependency>  <groupId>org.json</groupId>
  <artifactId>json</artifactId>
  <version>20090211</version>
 </dependency>

测试类

    @Test
    public void testJSONObject(){
        BasicConfigurator.configure();
        JSONObject jsonObject = new JSONObject();
        Object nullObject= null;//使编译器跳过对null类型对检查
        try {
            jsonObject.put("name","张全蛋");
            jsonObject.put("age","25.2");
            jsonObject.put("birthday","1990-01-01");
            jsonObject.put("school","富土康");
            jsonObject.put("major", new String[]{"理发", "挖掘机"});
            jsonObject.put("hasGirlFriend",false);
            jsonObject.put("hascar",nullObject);
            jsonObject.put("comment","这是一个注释,因为json格式里是不能使用常规注释手段的");

            System.out.println(jsonObject.toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        //output
        //{"birthday":"1990-01-01","major":["理发","挖掘机"],"school":"富土康","name":"张全蛋","comment":"这是一个注释,因为json格式里是不能使用常规注释手段的","hasGirlFriend":false,"age":"25.2"}
    }
}

输出

  {
    "birthday": "1990-01-01",
    "major": [
      "理发",
      "挖掘机"
    ],
    "school": "富土康",
    "name": "张全蛋",
    "comment": "这是一个注释,因为json格式里是不能使用常规注释手段的",
    "hasGirlFriend": false,
    "age": "25.2"
  }

Json在Java中使用字符串生成JSONObject对象

@Test
public void createJSONObjectByString(){
    JSONObject jsonObject = null;
    String jsonStr ="{\"errno\":0,\"data\":true,\"errmsg\":\"success\"}";
    try {
        jsonObject = new JSONObject(jsonStr);
    } catch (JSONException e) {
        e.printStackTrace();
    }
    if(jsonObject != null){
        System.out.println(jsonObject.toString());
        //out:
        // {"errno":0,"data":true,"errmsg":"success"}
    }
}

Json在Java中使用MAP生成JSONObject对象


    @Test
    public void createJSONObjectByMap() {
        Object nullObject = null;//使编译器跳过对null类型对检查

        Map<String, Object> jsonObject = new HashMap<String, Object>();
        jsonObject.put("name", "张全蛋");
        jsonObject.put("age", "25.2");
        jsonObject.put("birthday", "1990-01-01");
        jsonObject.put("school", "富土康");
        jsonObject.put("major", new String[]{"理发", "挖掘机"});
        jsonObject.put("hasGirlFriend", false);
        jsonObject.put("hascar", nullObject);
        jsonObject.put("comment", "这是一个注释,因为json格式里是不能使用常规注释手段的");

        System.out.println(new JSONObject(jsonObject).toString());

        //out:{"birthday":"1990-01-01","major":["理发","挖掘机"],"school":"富土康","name":"张全蛋","comment":"这是一个注释,因为json格式里是不能使用常规注释手段的","hasGirlFriend":false,"age":"25.2","hascar":null}
    }

Json在Java中使用JavaBean对象来生成JSONObject对象

    @Test
    public void createJSONObjectbyJavaBean() {
        //常用
        Message msg = new Message();
        msg.errno = 0;
        msg.errmsg = "success";
        msg.data = true;
        System.out.println(new JSONObject(msg).toString());

        //out:
        //{"errno":0,"data":true,"errmsg":"success"}
    }

从文件读取JSON数据

依赖

    <!--org.apache.commons.io.FileUtils-->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>

测试文件的内容 与测试类文件同级 名为success_message.json

    {
        "errno": 0,
        "errmsg":["智商余额不足","需发红包给刘老师@95820608进行充值!"],
        "data":true
    }

测试类中的测试方法


    @Test
    public void readJSONFile() throws IOException, JSONException {
        //从当前类的同源文件中查找特定路径的文件
        File file = new File(ReadJSONFileSample.class.getResource("/success_message.json").getFile());
        String content = FileUtils.readFileToString(file);
        JSONObject jsonObject = new JSONObject(content);
        if(!jsonObject.isNull("errno")){
            System.out.println("错误代码 --> " + jsonObject.getInt("errno"));
        }
        if(!jsonObject.isNull("errmsg")){
            System.out.println("错误信息 --> " + jsonObject.getString("errmsg").toString());
        }
        if(!jsonObject.isNull("data")){
            System.out.println("错误内容 --> " + jsonObject.getBoolean("data"));
        }
        //out
        //错误代码 --> 0
        //错误信息 --> ["智商余额不足","需发红包给刘老师@95820608进行充值!"]
        //错误内容 --> true

        //将JSONObject中的属性值为数组的值读取出来遍历
        //如果值不是数组则会导致报错!
        if(!jsonObject.isNull("errmsg")){
            JSONArray jsonArray = jsonObject.getJSONArray("errmsg");
            for (int i = 0; i < jsonArray.length(); i++) {
                String m = (String)jsonArray.get(i);
                System.out.println(m);
            }
        }
        //out
        //智商余额不足
        //需发红包给刘老师@95820608进行充值!
    }
}

使用Gson 来更加灵活的构建json数据

gson是google提供的开源api
Gson功能更强大可以在json格式和javaObject之间通过反射灵活转换
更适合服务端后台的开发

  • 优点:
  • 1.轻量, 支持将json字符串反向生成指定类的对象
  • 2.支持日期格式

使用Gson生成json

pom.xml 添加依赖

<!--GOOGLE GSON-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.4</version>
</dependency>

测试类和测试方法


    //使用GSON注解更改序列化后JSON的属性名称
    protected class Message{
        @SerializedName("ERRNO")
        int errno;
        String errmsg;
        boolean data;
    }

    @Test
    public void gsonCreate(){
        Message msg = new Message();
        msg.errno = 0;
        msg.errmsg = "success";msg.data = true;

        Gson gson = new Gson();
        String jsonStr = gson.toJson(msg);
        System.out.println(jsonStr);
        //out:{"ERRNO":0,"errmsg":"success","data":true}
    }

使用gsonBuilder定制json的转换规则

    protected class FailMessage{
        private int errno;
        private String errmsg;
        private boolean data;
        private transient String  ignor;//transient 声明的属性不会被序列化
    }

    @Test
    public void gsonCreateByGsonBuilder(){
        FailMessage msg = new FailMessage();
        msg.errno = -1;
        msg.errmsg = "fail";msg.data = false;

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();//美化格式后输出!方便调试
        gsonBuilder.setFieldNamingStrategy(new FieldNamingStrategy() {
        //通过回调函数修改反射得到的属性值
            @Override
            public String translateName(Field field) {
                if(field.getName().equals("errno")){
                    return ("error-code");
                }else{
                    return field.getName();
                }
            }
        });
        Gson gsonNew = gsonBuilder.create();
        System.out.println(gsonNew.toJson(msg));////美化格式后输出!方便调试
        //out:
        //        {
        //              "error-code": -1, //errno被修改为error-code
        //              "errmsg": "fail",
        //              "data": false
        //        }
    } 

使用gson反向将json字串转换成指定类

message.json文件内容如下:

    {
        "errno": 0,
        "errmsg":"智商余额不足,需发红包给刘老师@95820608进行充值!",
        "data":true
    }

测试类

public class Message {
  public int errno;
  public String errmsg;
  public boolean data;

    public int getErrno() {
        return errno;
    }

    public void setErrno(int errno) {
        this.errno = errno;
    }

    public String getErrmsg() {
        return errmsg;
    }

    public void setErrmsg(String errmsg) {
        this.errmsg = errmsg;
    }

    public boolean getData() {
        return data;
    }

    public void setData(boolean data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Message{" +
                "errno=" + errno +
                ", errmsg='" + errmsg + '\'' +
                ", data=" + data +
                '}';
    }
}

测试方法

    @Test
    public void GsonReadTest() throws IOException {
        File file = new File(GsonSample.class.getResource("/message.json").getFile());
        String content = FileUtils.readFileToString(file);

        Gson gson = new Gson();
        //将json文件转换成指定类
        Message msg = gson.fromJson(content, com.niit.mvcdemo.model.Message.class);
        System.out.println(msg.toString());
        //out:
        //Message{errno=0, errmsg='智商余额不足,需发红包给刘老师@95820608进行充值!', data=true}
    }

反向将json字串转换成指定带有时间属性的类

    class Logger{
        private String content;
        private Date createtime;
        @Override
        public String toString() {
            return "Logger{content='" + content + '\'' + ", createtime=" + createtime.toString() +'}';
        }
    }

    @Test
    public void GsonFromJsonToObject() throws IOException {

        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

        //使用gsonBuilder指定时间类型格式将json文件转换成指定类
        Logger logger = gson.fromJson("{\"content\":\"Application started...\",\"createtime\":\"1970-01-01 00:00:00\"}", json.GsonSample.Logger.class);
        System.out.println(logger.toLocalString());
        //out:Logger{content='Application started...', createtime=1970-1-1 0:00:00}
    }
小知识
yyyy-MM-dd HH:mm:ss
年-月-日 时:分:秒
大写是为了区分“月”与“分”
顺便说下HH为什么大写,是为了区分12小时制与24小时制。
小写的h是12小时制,大写的H是24小时制。
书写格式和语言规定有关,上述写法是Windows系统中的我们常见的写法,包括日期设置于办公软件在内。在其他语言中有类似的但使用符号或格式不同的写法。
有的时候我们会看到这样的格式:yyyy-M-d H:m:s
mm与m等,它们的区别为是否有前导零:H,m,s表示非零开始,HH,mm,ss表示从零开始。
比如凌晨1点2分,HH:mm显示为01:02,H:m显示为1:2。
以2014年1月1日凌晨1点1分1秒(当天是星期三)为例子介绍一下其他的:
yyyy/yyy/yy/y 显示为 2014/2014/14/4
        (3个y与4个y是一样的,为了便于理解多写成4个y)
MMMM/MMM/MM/M 显示为 一月/一月/01/1
        (4个M显示全称,3个M显示缩写,不过中文显示是一样的,英文就是January和J)
dddd/ddd/dd/d 显示为 星期三/周三(有的语言显示为“三”)/01/1
        (在英文中同M一样,4个d是全称,3个是简称;
dddd/ddd表示星期几,dd/d表示几号)
HH/H/hh/h 显示为 01/1/01 AM/1 AM
剩下的mm/m/ss/s只是前导零的问题了。
yyyy/M/d/dddd H:mm:ss 就是 2014年1月1日星期三 1:01:01

gson转换会自动将数组值映射成实体类对应的集合属性

注意Map会被映射成google自定义的Map类型..

class Product{
    private String title;
    private String[] array;
    private List list;
    private Set set;
    private Map map;
}

@Test
public void GsonMappingForCollection(){
    String jsonStr= "" +
            "{\n" +
            "  \"title\": \"product_id_1\",\n" +
            "  \"array\": [\n" +
            "    \"array1\",\n" +
            "    \"array2\"\n" +
            "  ],\n" +
            "  \"list\": [\n" +
            "    \"list1\",\n" +
            "    \"list2\"\n" +
            "  ],\n" +
            "  \"set\": [\n" +
            "    \"set2\",\n" +
            "    \"set1\"\n" +
            "  ],\n" +
            "  \"map\": {\n" +
            "    \"1\": \"map1\",\n" +
            "    \"2\": \"map2\"\n" +
            "  }\n" +
            "}"
            ;
    //通过gson将制定类的实例转化成json字符串
    Product product = new Gson().fromJson(jsonStr,GsonSample.Product.class);
    System.out.println(product.title.toString() + "\t\t" + product.title.getClass());
    //out:        product_id_1          class java.lang.String
    System.out.println(Arrays.toString(product.array) + "\t\t" + product.array.getClass());
    //out:        [array1, array2]      class [Ljava.lang.String;
    System.out.println(product.set.toString() + "\t\t" + product.set.getClass());
    //out:        [set2, set1]          class java.util.LinkedHashSet
    System.out.println(product.list.toString() + "\t\t" + product.list.getClass());
    //out:        [list1, list2]        class java.util.ArrayList
    System.out.println(product.map.toString() + "\t\t" + product.map.getClass());
    //out:        {1=map1, 2=map2}      class com.google.gson.internal.LinkedTreeMap
}

jackson实现Json序列化和反序列化

利用fasterxml.jackson实现JSON序列化和反序列化

Gradle依赖
fasterxml.jackson依赖jackson-core, jackson-databind和jackson-annotations.
示例如下:

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.5'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.5'

序列化和反序列化实现

JSON工具类
我们构造一个JSON工具类,实现JSON序列化和反序列化, 主要用到的类为com.fasterxml.jackson.databind.ObjectMapper, 代码示例如下:

package com.notepad.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class JsonSerializer {

    /**
     * JSON序列化
     *
     * @param object 对象
     * @return JSON字符串
     */
    public static String serialize(Object object) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * JSON字符串反序列化
     *
     * @param jsonStr JSON字符串
     * @return a Map
     */
    public static Map deserialize(String jsonStr) {
        try {
            return deserialize(jsonStr, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
            return new HashMap();
        }
    }

    public static <T> T deserialize(String jsonStr, Class<T> classType) throws Exception {
        return new ObjectMapper().readValue(jsonStr, classType);
    }
}

Entity
为测试我们编写的JSON工具类, 定义一个Entity对象。
为了构建的Entity对象被JSON工具类操作, 关于Entity有几点说明:

  • Entity对象必须有默认构造函数
  • 成员变量必须有对应的Setter方法
  • 可选: 可通过@JsonProperty自定义序列化和反序列化对应的字符的名称,如@JsonProperty(“UID”),则序列化时uid字段显示为UID,同理反序列化时找到字符串中UID对应的值,复制给uid。
  • 可选:可通过@JsonIgnore注解,过滤掉不需要进行序列化的成员变量。

示例如下:

package com.notepad.thinkingnote.domain;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Entity {

    public Entity() {}

    public Entity(String uid, String name) {
        this.uid = uid;
        this.name = name;
    }

    /** 实体标识符 */
    @JsonProperty("UID")
    private String uid;

    @JsonProperty("name")
    private String name;

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUid() {
        return uid;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

单元测试

编写单元测试,测试Entity的序列化和反序列化, 示例如下:

package com.notepad.thinkingnote.domain;

import com.notepad.util.JsonSerializer;
import org.junit.Test;

import static org.junit.Assert.*;

public class EntityTest {

    @Test
    public void testSerialize() throws Exception {
        Entity entity = new Entity("James", "James");
        System.out.println(JsonSerializer.serialize(entity));
    }

    @Test
    public void testDeserialize() throws Exception {
        String test = "{\"UID\":\"James\",\"name\":\"James\"}";
        Entity entity = JsonSerializer.deserialize(test, Entity.class);
        System.out.println(entity.getUid() + ":" + entity.getName());
    }
}

输出结果如下:
entity的序列化结果, 显示字段UID
{"UID":"James","name":"James"}

entity的序列化结果
James:James

UnrecognizedPropertyException异常解决

通过上面的介绍, 我们基本了解了JSON的序列化和反序列化,不过有时我们会遇到一种问题。
考虑这样一种情况,我们针对Http接口返回的JSON字符串,构建了一个具体的Entity对象,但是当Http接口中突然增加了一个字段type,如果还是按照原来方式解析会如何呢?

@Test
    public void testDeserialize() throws Exception {
        String test = "{\"UID\":\"James\",\"name\":\"James\", \"type\":\"entity\"}";
        Entity entity = JsonSerializer.deserialize(test, Entity.class);
        System.out.println(entity.getUid() + ":" + entity.getName());
    }

出现UnrecognizedPropertyException异常:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "type" ....

即对于新添加的type字符无法识别。如何解决这个问题呢?我们这里提供2种方法。

  • 方法1 JsonIgnoreProperties注解
    利用fasterxml.jackson提供的@JsonIgnoreProperties注解,针对无法识别的属性进行过滤。这里主要是修改需要进行反序列化的对象Entity,示例如下:
    针对无法识别的属性进行过滤

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class Entity {...}
  • 方法2 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
    方法1的修改需要对每一个需要进行反序列化的类进行修改, 不太方便。
    方法2通过修改JSON工具类的反序列化方法,设置DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES的值为false, 可以仅一次修改就适用全部对象。
    示例如下:

    public static <T> T deserialize(String jsonStr, Class<T> classType) throws Exception {
        // 添加configure, 设置DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES为false
        // 则对于无法识别的属性直接过滤
        return new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .readValue(jsonStr, classType);
    }

Views: 19

Index