# JSON 数据格式

  • JSON(JavaScript Object Notation),JS 对象标记,数据交换格式

  • JSON 有两种结构

    1. 对象(object),表示一组无序的键值对,一个对象由 { } 括起来的,“名称/值”对之间使用 , 分隔,每个“名称”后跟一个 : ,格式例如 {"key1":value1, "key2":value2, "key3":value3, ...}(注意:对象的属性名必须加双引号
    2. 数组(array),表示一组有序的值,一个数组由 [ ] 括起来,值之间使用 , 分隔,格式例如 [{"key1":value1,"key2":value2,"key3":value3}, {"key1":value1,"key2":value2,"key3":value3}, ...]
  • JSON 值可以是:数字(整数或浮点数)、字符串(在双引号中)、逻辑值(true 或 false)、数组(在方括号中)、对象(在花括号中)、null

    {
        "Number": 123,
        "String": "Hello World",
        "Boolean": true,
        "Array": [1, 2, 3],
        "Object": {"a": "b", "c": "d"},
        "Null": null
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Firefox 浏览器中将对象或数组转换为 JSON 字符串的方法:对象或数组.toSource()

  • JSON 格式字符串转对象或数组的方法:eval("(" + jsonString + ")"),因为 JavaScript 规定,如果行首是花括号,一律解释为语句(即代码块),如果要解释为表达式(即对象),必须在花括号前加上圆括号

# Java 中操作 JSON 的库

# jackson (opens new window)

  • 所需 jar 包:jackson-core.jar、jackson-annotations.jar、jackson-databind.jar (opens new window)
  • JSON 与 Java 对象之间的转换:com.fasterxml.jackson.databind.ObjectMapper、com.fasterxml.jackson.databind.json.JsonMapper(jackson-databind.jar)
  • XML 与 Java 对象之间的转换:com.fasterxml.jackson.dataformat.xml.XmlMapper(jackson-dataformat-xml.jar)
  • YAML 与 Java 对象之间的转换:com.fasterxml.jackson.dataformat.yaml.YAMLMapper(jackson-dataformat-yaml.jar)

# jackson 中的处理 JSON 的三种方式

  1. 数据绑定:JSON 和 POJO 相互转换,基于属性访问器规约或注解(最常用)

  2. 树模型:提供一个 JSON 文档可变内存树的表示形式(最灵活)

    • 容器节点抽象类 ContainerNode(extends BaseJsonNode(extends JsonNode))
    • 子类:ObjectNode、ArrayNode
    JsonNode jsonNode = mapper.readTree(jsonStr);
    ObjectNode objNode = mapper.createObjectNode(); // 创建对象节点  
    ArrayNode arrNode = mapper.createArrayNode(); // 创建数组节点
    ArrayNode arrNode = objNode.withArray("propertyName")
    
    1
    2
    3
    4
  3. 流式 API:读取和写入 JSON 内容作为离散事件(性能最佳:开销最低、速度最快的读/写;其它二者基于它实现),相关类:JsonParser、JsonGenerator

# JSON 与 Java 对象之间的转换

  • 在默认情况下,ObjectMapper 依赖于 Java 对象的默认的无参构造器进行反序列化,并且严格地通过 getter 和 setter 的命名规约进行序列化和反序列化
    • jackson 反序列化时对象的属性类型不能为实例内部类,但可以为静态内部类(创建实例内部类对象前必须先创建外部类对象)
    • 在序列化时,要求 field 可以被访问到,先通过 getter,如果没有再去找 field,如果还没有,就跳过这个 field
    • 在反序列化时,通过反射,调用构造器,根据 json 里的 key-value 中的 key,去找对应变量的 setter,找不到就直接找对应变量,如果还找不到且没有设置 ignore unknown,就抛出异常
  • MapperFeature#USE_STD_BEAN_NAMING(false): Specific difference is that Jackson always lower cases leading upper-case letters, so "getURL()" becomes "url" property; whereas standard Bean naming only lower-cases the first letter if it is NOT followed by another upper-case letter (so "getURL()" would result in "URL" property). BeanUtil#legacyManglePropertyNameBeanUtil#stdManglePropertyName
  • ObjectMapper 默认将 json 格式字符串中的对象结构转换为 LinkedHashMap 对象数组结构转换为 ArrayList 对象

  • com.fasterxml.jackson.databind.ObjectMapper 类中的实例方法

    • String writeValueAsString(Object value):对象或集合转 json 格式字符串

    • <T> T readValue(String content, Class<T> valueType):json 格式字符串转简单类型对象

    • <T> T readValue(String content, TypeReference valueTypeRef):json 格式字符串转复杂类型对象(如有泛型类型字段的对象),如 mapper.readValue(jsonStr, new TypeReference<Result<XXXX>>() {});

    • <T> T readValue(String content, JavaType valueType):json 格式字符串转复杂类型对象(如集合类型)

    • <T> T convertValue(Object fromValue, Class<T> toValueType):将给定值转换为指定类型的对象

    • 通过 ObjectMapper 获取类型工厂 TypeFactory mapper.getTypeFactory(),再通过 TypeFactory 构造 JavaType(或其子类 ArrayType、CollectionType、MapType)

      • JavaType constructType(TypeReference<?> typeRef)
      • JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses)
      • JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes)
      • ArrayType constructArrayType(Class<?> elementType)
      • ArrayType constructArrayType(JavaType elementType)
      • CollectionType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass)
      • CollectionType constructCollectionType(Class<? extends Collection> collectionClass, JavaType elementType)
      • MapType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass)
      • MapType constructMapType(Class<? extends Map> mapClass, JavaType keyType, JavaType valueType)
      /**
       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
       */
      
      JavaType javaType = mapper.getTypeFactory().constructParametricType(Result.class, User.class);
      Result<User> result = mapper.readValue(jsonStr, javaType);
      // List<User> userList = mapper.readValue(jsonStr, new TypeReference<Result<User>>() {});
      
      ArrayType arrayType = mapper.getTypeFactory().constructArrayType(User.class);
      User[] users = mapper.readValue(jsonStr, arrayType);
      // User[] o = mapper.readValue(jsonStr, new TypeReference<User[]>() {});
      
      CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, User.class);
      List<User> userList = mapper.readValue(jsonStr, listType);
      // List<User> userList = mapper.readValue(jsonStr, new TypeReference<List<User>>() {});
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
  • ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility):设置可见性

  • ObjectMapper enableDefaultTyping(DefaultTyping dti):指定序列化时包含的属性类型信息(默认不开启,即不包含类型信息)

  • ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)

  • ObjectMapper configure(DeserializationFeature f, boolean state):打开/关闭某反序列化特性,如浮点数反序列化为 Double、忽略空属性等

    // 通过反射机制(而非 getter 和 setter 方法)直接操作对象上的字段
    mapper.findAndRegisterModules()
        // 屏蔽所有 accessor
        .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
        // 任何字段可见
        .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    
    mapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 格式化输出
    // mapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true); // 默认 false
    // mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); // 属性序列化顺序
    mapper.disable(DeserializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 禁用序列化日期为 timestamps
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 禁用遇到未知属性抛出异常
    
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
    // {"@class":"org.apache.commons.lang3.tuple.MutableTriple","left":"1234","middle":["java.lang.Long",4562],"right":"triple"}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# jackson 的多态类型处理 (opens new window)

方式一:设置全局的 DefaultTyping;方式二:使用 @JsonTypeInfo 注解

  1. 全局 DefaultTyping,ObjectMapper.DefaultTyping 枚举类中的枚举值:

    • JAVA_LANG_OBJECT:序列化时包含属性类型为 Object 的类型信息
    • OBJECT_AND_NON_CONCRETE:序列化时包含属性类型为 Object、非具体类型(抽象类和接口)的类型信息
    • NON_CONCRETE_AND_ARRAYS:序列化时包含属性类型为 Object、非具体类型(抽象类和接口)以及数组元素类型的类型信息
    • NON_FINAL:序列化时包含非 final 对象类型信息、以及属性中所有非 final 类型或者非 final 类型数组元素的类型信息
  2. 使用注解处理多态

    • @JsonTypeInfo:修饰类、字段、方法、参数,用于指出序列化包含的类型信息细节

      • 属性:
        • use:(必选)指定序列化类型信息时使用的类型识别码,属性值为 JsonTypeInfo.Id 中的枚举值:
          • CLASS:使用完全限定类名做识别,此时默认的识别码属性名称 "@class"
          • MINIMAL_CLASS:若基类和子类在同一包类,使用类名(忽略包名)作为识别码,此时默认的识别码属性名称 "@c"
          • NAME:使用 @JsonSubTypes 或 @JsonTypeName 指定的逻辑类型名称,此时默认的识别码属性名称 "@type"
          • CUSTOM:自定义识别码,需结合 property 属性和 @JsonTypeIdResolver
          • NONE:不使用识别码,即序列化是不包含类型信息
        • include:指定识别码是如何被包含进去的,属性值为 JsonTypeInfo.As 中的枚举值:
          • PROPERTY:作为数据的属性(默认)
          • EXISTING_PROPERTY:作为 POJO 中已经存在的属性
          • EXTERNAL_PROPERTY:作为扩展属性
          • WRAPPER_OBJECT:作为一个包装的对象
          • WRAPPER_ARRAY:作为一个包装的数组
        • property:指定识别码的属性名称
        • defaultImpl:如果类型识别码不存在或者无效,指定反序列化时使用的默认类型
        • visible:定义识别码在反序列化时是否保留,默认 false
    • @JsonSubTypes:用来指示该类的子类型以及逻辑类型名称

    • @JsonTypeName:用于定义类的逻辑类型名称

    只有当 @JsonTypeInfo 的 use 属性值为 JsonTypeInfo.Id.NAME 时,才会使用逻辑类型名称

    @Data
    public class Zoo {
        private Animal animal;
    }
    
    1
    2
    3
    4
    @Data
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Dog.class, name = "dog"),
            @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    public abstract class Animal {
        protected String name;
        protected String type;
    }
    
    @Data
    public class Dog extends Animal {
        private Double barkVolume;
    }
    
    @Data
    public class Cat extends Animal {
        private Boolean likesCream;
        private Integer lives;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
        "animals": {
            "name": "lacy",
            "type": "cat",
            "likesCream": true,
            "lives": 5
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    // @JsonSubTypes 可以用其它方式代替
    @Data
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
    public abstract class Animal {
        protected String name;
        protected String type;
    }
    
    @Data
    @JsonTypeName("dog")
    public class Dog extends Animal {
        private Double barkVolume;
    }
    
    @Data
    @JsonTypeName("cat")
    public class Cat extends Animal {
        private Boolean likesCream;
        private Integer lives;
    }
    
    mapper.registerSubtypes(Animal.class, Dog.class, Cat.class); // 注册子类或逻辑类型名称
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

# Jackson 的常用注解 (opens new window)

  • @JsonInclude(JsonInclude.Include.NON_NULL),修饰类,序列化时忽略 null 属性,可配置 spring.jackson.default-property-inclusion=non_null
  • @JsonIgnoreProperties(value = {"internalId", "secretKey"}, ignoreUnknown = true),修饰类,指定时需要忽略的字段,且在反序列化时忽略 json 中存在的未知字段
  • @JsonIgnore,序列化或反序列化时忽略该属性
  • @JsonCreator,修饰构造器,指定反序列化时调用的构造器,默认调用无参构造器
  • @JsonProperty("uname"),属性值:value(指定反序列化和序列化时该属性对应的名称)、required(该属性是否必须存在 json 中)
  • @JsonAlias:指定反序列化时该属性对应的一个或多个别名
  • @JsonValue,修饰字段、方法,一个类中最多只能存在一个该注解,表示以该字段值或方法的返回值作为序列化结果
  • @JsonEnumDefaultValue,设置默认枚举值(需开启 read_unknown_enum_values_using_default_value=true)
  • @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8"),属性:pattern、timezone、shap(指定序列化后类型),该注解支持 Java 8 中新的日期和时间 API
  • @JsonSerialize,修饰字段或者 getter 方法上,用于在序列化时附加自定义的代码,与 @JsonFormat 类似,但是功能更丰富,支持自定义
  • @JsonDeserialize,修饰字段或者 setter 方法上,用于在反序列化时附加自定义的代码
  • @JsonNaming,修饰类,用于指定命名的策略,内置的命名策略(PropertyNamingStrategy):SNAKE_CASE、UPPER_CAMEL_CASE、LOWER_CAMEL_CASE(默认)、LOWER_CASE、KEBAB_CASE

int 类型枚举字段上标记 @JsonValue 只能用于序列化对反序列化无效,反序列化时使用的还是枚举的 ordinal() 索引值(相关 Issue (opens new window)

// 时间戳序列化、反序列化为 LocalDateTime
public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException {
        if (value != null) {
            long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            gen.writeNumber(timestamp);
        }
    }
}

public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
            throws IOException {
        long timestamp = p.getValueAsLong();
        if (timestamp > 0) {
            return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
        } else {
            return null;
        }
    }
}

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime timestamp;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# fastjson

  • 所需 jar 包:fastjson.jar
  • 注意:转对象时,该对象的类需提供 setter 方法,转字符串时,根据对象的类提供的 getter 方法
  • fastjson 默认将 json 格式字符串中的对象结构转换为 JSONObject 对象(implements Map<String, Object>),数组结构转换为 JSONArray对象(implements List<Object>)
  • JSON 类中的类方法
    • String toJSONString(Object object):对象或集合序列化为 json 格式字符串
    • <T> T parseObject(String text, Class<T> clazz):json 格式字符串反序列化为简单类型对象
    • <T> T parseObject(String text, TypeReference<T> type, Feature... features):json 格式字符串反序列化为复杂类型对象,如 List<Model> models = JSON.parseObject(jsonStr, new TypeReference<List<Model>>() {});
    • JSONObject parseObject(String text):json 格式字符串转为 JSONObject(JSONObject 是 json 字符串与 POJO 对象转换过程中的中间表达类型,实现了 Map 接口,Xxx getXxx(String key)xxx getXxxValue(String key)
    • <T> List<T> parseArray(String text, Class<T> clazz):json 格式字符串反序列化为 List 集合类型对象
  • JSONArray parseArray(String text):json 格式字符串转为 JSONArray(JSONObject 是 JSON 字符串与 List 集合类型对象转换过程中的中间表达类型,实现了 List 接口,Xxx getXxx(int index)xxx getXxxValue(int index)
  • fastjson 的常用注解
    • @JSONField,修饰字段、getter 或 setter 方法,属性:ordinal、name(转 json 格式时的属性名)、alternateNames(反序列化时字段的替代名称)、format、serialize(转 json 格式时是否忽略)、deserialize、serialzeFeatures = {SerializerFeature.WriteMapNullValue}(序列化 value 为 null 的字段)
    • @JSONType,修饰类,属性:includes、ignores、naming
  • JSONPath

# Gson

# JSON 的最佳实践

  • 在实体类中提供 Map<String, Object> toJson() 方法,Map 中放置需要转换成 json 格式的属性及属性值,在 Controller 中的请求处理方法返回该 Map 对象
Updated at: 2022-02-19 11:02:38