10、JSON

JSON

JSON(JavaScript Object Notation,JS对象简谱)是一种轻量级的数据交换格式。它基于 ECMAScript(欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据,但是也使用了类似于 C 语言家族的习惯(包括 C, C++, C#, Java, JavaScript, Perl, Python 等)。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON的本质就是一个对象序列化后的字符串

JSON广泛应用于Web开发和数据交换。它是一种通用的数据格式,常用于前后端之间的数据传输,如将服务器返回的数据转换为JSON格式在客户端进行处理。

在Java中,常用的JSON库包括Jacksonfastjson等,它们提供了方便的API来解析、生成和操作JSON数据。

语法

JSON使用类似于JavaScript的语法规则,包括对象和数组的表示方式。

{
  "name": "John",
  "age": 30,
  "isStudent": true,
  "address": {
    "street": "123 Main St",
    "city": "New York"
  },
  "hobbies": ["reading", "coding", "traveling"]
}

数据类型

字符串

"Hello, World!"

数字

42
3.14

布尔

true
false

对象

由键值对组成的无序集合。

{
  "name": "John",
  "age": 30
}

数组

由值组成的有序集合

["red", "green", "blue"]

空值

null

JavaScript解析JSON

// json字符串转js对象
var jsObj = JSON.parse(jsonStr)

// js对象转json字符串
var jsonStr = JSON.stringify(jsObj)

Jackson

Jackson是Java中一个流行的JSON处理库,它提供了一组功能强大的API,用于解析、生成和操作JSON数据,具有以下特点

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.6</version>
</dependency>

Jackson 最常用的 API 就是基于对象绑定的 ObjectMapper

public class Stu {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

JSON转Object

String json = "{\"name\": \"lucy\",\"age\": 18}";

ObjectMapper objectMapper = new ObjectMapper();

// 字符串转Object
Stu s1 = objectMapper.readValue(json, Stu.class);
System.out.println(s1);

// 字符输入流转Object
StringReader reader = new StringReader(json);
Stu s2 = objectMapper.readValue(reader, Stu.class);
System.out.println(s1);

// json文件转Object
File file = new File("stu.json");
Stu s3 = objectMapper.readValue(file, Stu.class);
System.out.println(s3);

// url读取json转Object
URL url = new URL("http://localhost/stu.json");
Stu s4 = objectMapper.readValue(url, Stu.class);
System.out.println(s4);

// 字节输入流转Object
InputStream inputStream = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
Stu s5 = objectMapper.readValue(inputStream, Stu.class);
System.out.println(s5);

// 字节数组转Object
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
Stu s6 = objectMapper.readValue(bytes, Stu.class);
System.out.println(s6);


String jsonArray = "[\"lucy\",\"tom\",\"grady\"]";

// json转List
List<String> list = objectMapper.readValue(jsonArray, new TypeReference<List<String>>() {});
System.out.println(list);

// json转Map
Map<String,Object> map = objectMapper.readValue(json,new TypeReference<Map<String,Object>>() {});
System.out.println(map);

忽略未知的JSON字段

有时候,与要从JSON读取的Java对象相比,JSON中的字段更多。 默认情况下,Jackson在这种情况下会抛出异常UnrecognizedPropertyException,因为在Java对象中找不到该字段。

但是,有时应该允许JSON中的字段多于相应的Java对象中的字段。 例如,要从REST服务解析JSON,而该REST服务包含的数据远远超出所需的。 在这种情况下,可以使用Jackson配置忽略这些额外的字段。 以下是配置Jackson ObjectMapper忽略未知字段的示例:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

不允许基本数据类型为null

如果Java对象的某个属性为基本数据类型,但是对应的json中该属性为null,那么Jackson默认会忽略这个字段。我们可以通过配置,在基本属性值为null的情况下,抛出异常

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

Object转JSON

ObjectMapper objectMapper = new ObjectMapper();

Stu stu = new Stu();
stu.setAge(18);
stu.setName("lucy");

String s = objectMapper.writeValueAsString(stu);
System.out.println(s);

日期转化

默认情况下,Jackson会将java.util.Date对象序列化为其long型的值,该值是自1970年1月1日以来的毫秒数。

public class MyObj {
    
    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}
MyObj myObj = new MyObj();
myObj.setDate(new Date());

ObjectMapper objectMapper = new ObjectMapper();
// 序列化
String s = objectMapper.writeValueAsString(myObj);
System.out.println(s); // {"date":1689043359773}
// 反序列化
MyObj mo = objectMapper.readValue(s, MyObj.class);
System.out.println(mo); // MyObj{date=Tue Jul 11 10:43:35 CST 2023}

对于实体类如果需要序列化以及序列化使用自定义格式,那么可以使用@JsonFormat注解进行定义,该注解可以作用于方法、属性。

public class MyObj {

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")
    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}
MyObj myObj = new MyObj();
myObj.setDate(new Date());

ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(myObj);
System.out.println(s); // {"date":"2023-07-11 10:49:02"}

MyObj mo = objectMapper.readValue(s, MyObj.class);
Date date = mo.getDate();
System.out.println(mo); // MyObj{date=Tue Jul 11 10:49:02 CST 2023}

自定义序列化器

例如自定义一个Double类型保留两位小数的序列化器

//修改JsonSerializer<Double> 到需要的类型,默认为JsonSerializer,参数为Object value
public class JsonSerializerUtils extends JsonSerializer<Double> {
    @Override
    public void serialize(Double value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (Objects.nonNull(value)) {
            //保留2位小数#代表末位是0舍去
            DecimalFormat decimalFormat = new DecimalFormat("0.##");
            //四舍五入
            decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
            String result = decimalFormat.format(value);
            jsonGenerator.writeNumber(Double.valueOf(result));
        } else {
            jsonGenerator.writeNumber(Double.valueOf(0));
        }
    }
}

在需要进行格式化的实体类属性上添加注解

@JsonSerialize(using = JsonSerializerUtils.class)
private Double waterPrice;

常用注解

@JsonIgnore

作用范围:序列化及反序列化

Jackson注解@JsonIgnore用于告诉Jackson忽略Java对象的某个属性(字段)。

public class Stu {
    private String name;
	@JsonIgnore
    private int age;
}

@JsonIgnoreProperties

作用范围:序列化及反序列化

用于指定要忽略的类的属性列表。

@JsonIgnoreProperties({"id","age"})
public class Stu {
    private int id;
    private String name;
    private int age;
}

@JsonIgnoreType

作用范围:序列化及反序列化

用于将整个类型(类)标记为在使用该类型的任何地方都将被忽略。

@JsonIgnoreType
public class Stu {
    private String name;
    private int age;
}

@JsonAutoDetect

用于告诉Jackson在读写对象时包括非public修饰的属性。

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
public class Stu {
    private String name;
    public int age;
}

Fastjson

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.7</version>
</dependency>

如果对象中属性为null,则不转化

JAVAObject转JSON

String json = JSON.toJSONString(javaObject);

JSON转JAVAObject

需要该类具有无参构造器

Person newPerson = JSON.parseObject(jsonString, Person.class);

List转JSONObject

JSONArray jsonArray = (JSONArray)JSON.toJSON(list);