Java中的JSON序列化和反序列化

简介: Java中的JSON序列化和反序列化

Java 和 JSON 序列化

JSON(JavaScript Object Notation)是一种基于文本的数据交换格式。几乎所有的编程语言都有很好的库或第三方工具来提供基于 JSON 的 API 支持,因此你可以非常方便地使用任何自己喜欢的编程语言来处理 JSON 数据。

JSON 简介

JSON 是什么

JSON 起源于 1999 年的 JS 语言规范 ECMA262 的一个子集,后来 2003 年作为一个数据格式ECMA404(404???)发布。

2006 年,作为 rfc4627 发布,这时规范增加到 18 页,去掉没用的部分,十页不到。

JSON 的应用很广泛,这里有超过 100 种语言下的 JSON 库:json.org

更多的可以参考这里,关于 json 的一切

JSON 标准

这是最简单标准规范之一:

  • 只有两种结构:对象内的键值对集合结构和数组,对象用 {} 表示、内部是 "key":"value",数组用 [] 表示,不同值用逗号分开
  • 基本数值有 7 个: false / null / true / object / array / number / string
  • 再加上结构可以嵌套,进而可以用来表达复杂的数据
  • 一个简单实例:
{
  "Image": {
    "Width": 800,
    "Height": 600,
    "Title": "View from 15th Floor",
    "Thumbnail": {
      "Url": "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
      "Height": 125,
      "Width": "100"
    },
    "IDs": [116, 943, 234, 38793]
  }
}

JSON 优缺点

优点:

  • 基于纯文本,所以对于人类阅读是很友好的。
  • 规范简单,所以容易处理,开箱即用,特别是 JS 类的 ECMA 脚本里是内建支持的,可以直接作为对象使用。
  • 平台无关性,因为类型和结构都是平台无关的,而且好处理,容易实现不同语言的处理类库,可以作为多个不同异构系统之间的数据传输格式协议,特别是在 HTTP/REST 下的数据格式。

缺点:

  • 性能一般,文本表示的数据一般来说比二进制大得多,在数据传输上和解析处理上都要更影响性能。
  • 缺乏 schema,跟同是文本数据格式的 XML 比,在类型的严格性和丰富性上要差很多。XML 可以借由 XSD 或 DTD 来定义复杂的格式,并由此来验证 XML 文档是否符合格式要求,甚至进一步的,可以基于 XSD 来生成具体语言的操作代码,例如 apache xmlbeans。并且这些工具组合到一起,形成一套庞大的生态,例如基于 XML 可以实现 SOAP 和 WSDL,一系列的 ws-*规范。但是我们也可以看到 JSON 在缺乏规范的情况下,实际上有更大一些的灵活性,特别是近年来 REST 的快速发展,已经有一些 schema 相关的发展(例如理解 JSON Schema使用 JSON Schema在线 schema 测试),也有类似于 WSDL 的WADL出现。

JSON 工具

Java JSON 库

Java 中比较流行的 JSON 库有:

  • Fastjson - 阿里巴巴开发的 JSON 库,性能十分优秀。
  • Jackson - 社区十分活跃且更新速度很快。Spring 框架默认 JSON 库。
  • Gson - 谷歌开发的 JSON 库,目前功能最全的 JSON 库 。

从性能上来看,一般情况下:Fastjson > Jackson > Gson

JSON 编码指南

遵循好的设计与编码风格,能提前解决 80%的问题,推荐 Google JSON 风格指南。

简单摘录如下:

  • 属性名和值都是用双引号,不要把注释写到对象里面,对象数据要简洁
  • 不要随意结构化分组对象,推荐是用扁平化方式,层次不要太复杂
  • 命名方式要有意义,比如单复数表示
  • 驼峰式命名,遵循 Bean 规范
  • 使用版本来控制变更冲突
  • 对于一些关键字,不要拿来做 key
  • 如果一个属性是可选的或者包含空值或 null 值,考虑从 JSON 中去掉该属性,除非它的存在有很强的语义原因
  • 序列化枚举类型时,使用 name 而不是 value
  • 日期要用标准格式处理
  • 设计好通用的分页参数
  • 设计好异常处理

JSON API与 Google JSON 风格指南有很多可以相互参照之处。

JSON API是数据交互规范,用以定义客户端如何获取与修改资源,以及服务器如何响应对应请求。

JSON API 设计用来最小化请求的数量,以及客户端与服务器间传输的数据量。在高效实现的同时,无需牺牲可读性、灵活性和可发现性。

Fastjson 应用

添加 maven 依赖

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

Fastjson API

定义 Bean

Group.java

public class Group {
    private Long       id;
    private String     name;
    private List<User> users = new ArrayList<User>();
}

User.java

public class User {
    private Long   id;
    private String name;
}

初始化 Bean

Group group = new Group();
group.setId(0L);
group.setName("admin");
User guestUser = new User();
guestUser.setId(2L);
guestUser.setName("guest");
User rootUser = new User();
rootUser.setId(3L);
rootUser.setName("root");
group.addUser(guestUser);
group.addUser(rootUser);
序列化
String jsonString = JSON.toJSONString(group);
System.out.println(jsonString);
反序列化
Group bean = JSON.parseObject(jsonString, Group.class);

Fastjson 注解

@JSONField

扩展阅读:更多 API 使用细节可以参考:JSONField 用法,这里介绍基本用法。

可以配置在属性(setter、getter)和字段(若属性是私有的,必须有set*方法。否则无法反序列化)上。

@JSONField(name="ID")
public int getId() {return id;}
// 配置date序列化和反序列使用yyyyMMdd日期格式
@JSONField(format="yyyyMMdd")
public Date date1;
// 不序列化
@JSONField(serialize=false)
public Date date2;
// 不反序列化
@JSONField(deserialize=false)
public Date date3;
// 按ordinal排序
@JSONField(ordinal = 2)
private int f1;
@JSONField(ordinal = 1)
private int f2;
@JSONType

JSONType.alphabetic 属性: fastjson 缺省时会使用字母序序列化,如果你是希望按照 javafields/getters 的自然顺序序列化,可以配置 JSONType.alphabetic,使用方法如下:

@JSONType(alphabetic = false)
public static class B {
    public int f2;
    public int f1;
    public int f0;
}

Jackson 应用

扩展阅读:更多 API 使用细节可以参考 jackson-databind 官方说明

添加 maven 依赖

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

Jackson API

序列化
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(new File("result.json"), myResultObject);
// or:
byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
// or:
String jsonString = mapper.writeValueAsString(myResultObject);
反序列化
ObjectMapper mapper = new ObjectMapper();
MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
// or:
value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
// or:
value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
容器的序列化和反序列化
Person p = new Person("Tom", 20);
Person p2 = new Person("Jack", 22);
Person p3 = new Person("Mary", 18);
List<Person> persons = new LinkedList<>();
persons.add(p);
persons.add(p2);
persons.add(p3);
Map<String, List> map = new HashMap<>();
map.put("persons", persons);
String json = null;
try {
 json = mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
 e.printStackTrace();
}

Jackson 注解

扩展阅读:更多注解使用细节可以参考 jackson-annotations 官方说明

@JsonProperty
public class MyBean {
   private String _name;
   // without annotation, we'd get "theName", but we want "name":
   @JsonProperty("name")
   public String getTheName() { return _name; }
   // note: it is enough to add annotation on just getter OR setter;
   // so we can omit it here
   public void setTheName(String n) { _name = n; }
}
@JsonIgnoreProperties@JsonIgnore
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
// regardless of whether POJO has such properties
@JsonIgnoreProperties({ "foo", "bar" })
public class MyBean {
   // will not be written as JSON; nor assigned from JSON:
   @JsonIgnore
   public String internal;
   // no annotation, public field is read/written normally
   public String external;
   @JsonIgnore
   public void setCode(int c) { _code = c; }
   // note: will also be ignored because setter has annotation!
   public int getCode() { return _code; }
}
@JsonCreator
public class CtorBean {
  public final String name;
  public final int age;
  @JsonCreator // constructor can be public, private, whatever
  private CtorBean(@JsonProperty("name") String name,
    @JsonProperty("age") int age)
  {
      this.name = name;
      this.age = age;
  }
}
@JsonPropertyOrder

alphabetic 设为 true 表示,json 字段按自然顺序排列,默认为 false。

@JsonPropertyOrder(alphabetic = true)
public class JacksonAnnotationBean {}

Gson 应用

详细内容可以参考官方文档:Gson 用户指南

添加 maven 依赖

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

Gson API

序列化
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(10L); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]
反序列化
int i1 = gson.fromJson("1", int.class);
Integer i2 = gson.fromJson("1", Integer.class);
Long l1 = gson.fromJson("1", Long.class);
Boolean b1 = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
GsonBuilder

Gson 实例可以通过 GsonBuilder 来定制实例化,以控制其序列化、反序列化行为。

Gson gson = new GsonBuilder()
  .setPrettyPrinting()
  .setDateFormat("yyyy-MM-dd HH:mm:ss")
  .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
  .create();

Gson 注解

@Since

@Since 用于控制对象的序列化版本。示例:

public class VersionedClass {
  @Since(1.1) private final String newerField;
  @Since(1.0) private final String newField;
  private final String field;
  public VersionedClass() {
    this.newerField = "newer";
    this.newField = "new";
    this.field = "old";
  }
}
VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
System.out.println();
gson = new Gson();
jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
@SerializedName

@SerializedName 用于将类成员按照指定名称序列化、反序列化。示例:

private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;
  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

参考资料

  • 官方
  • 文章
相关文章
|
1天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
9 3
|
4天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
12天前
|
JSON JavaScript Java
在Java中处理JSON数据:Jackson与Gson库比较
本文介绍了JSON数据交换格式及其在Java中的应用,重点探讨了两个强大的JSON处理库——Jackson和Gson。文章详细讲解了Jackson库的核心功能,包括数据绑定、流式API和树模型,并通过示例演示了如何使用Jackson进行JSON解析和生成。最后,作者分享了一些实用的代码片段和使用技巧,帮助读者更好地理解和应用这些工具。
在Java中处理JSON数据:Jackson与Gson库比较
|
17天前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
|
23天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第3天】在Java编程的世界里,对象序列化与反序列化是实现数据持久化和网络传输的关键技术。本文将深入探讨Java序列化的原理、应用场景以及如何通过代码示例实现对象的序列化与反序列化过程。从基础概念到实践操作,我们将一步步揭示这一技术的魅力所在。
|
22天前
|
消息中间件 存储 Java
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
大数据-58 Kafka 高级特性 消息发送02-自定义序列化器、自定义分区器 Java代码实现
30 3
|
23天前
|
分布式计算 资源调度 Hadoop
Hadoop-10-HDFS集群 Java实现MapReduce WordCount计算 Hadoop序列化 编写Mapper和Reducer和Driver 附带POM 详细代码 图文等内容
Hadoop-10-HDFS集群 Java实现MapReduce WordCount计算 Hadoop序列化 编写Mapper和Reducer和Driver 附带POM 详细代码 图文等内容
75 3
|
3天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
6 0
|
JSON Java Maven
在Java中,我们可以使用第三方库来解析JSON数据
在Java中,我们可以使用第三方库来解析JSON数据。这里以常用的Jackson库为例,演示如何解析JSON数据。
89 1
|
JSON Java Maven