文章目录
1 JSON解析问题
1.1 User实体
首先假设有一个实体对象user:
private Integer UserId;
private Integer getUserId (){
return UserId;
}
private Integer setUserId(Integer UserId){
this.UserId = UserId;
}
用的是com.alibaba.fastjson.JSON的包,用的是toJSON方法
1.2 转换为JSON对象后首字母小写
1.2.1 使用普通getset方法
在实体对象的每个get方法上添加@JSONField(name=“UserId”)
就可以有效避免,首字母小写的问题
@JSONField(name="UserId")
private Integer getUserId (){
return UserId;
}
1.2.2 使用lombok导致失效
有一个接收类,出于某种原因(调用第三方接口)会使用首字母大写的情况
@Data
public class HelloModel{
private Strig ATest;
private Strig BTest;
}
当我使用这个类接收一个JSON
格式的数据,转换为对应的这个 HelloModel
类时,会出现 ATest
和 BTest
都为null
的情况
原因解析
由于
java
的驼峰规范
,构成JavaBean
都是以小写字母开始,框架和底层会自己去实现这个事情,在debug
的时候可以发现,
这两个字段名,都变成了aTest,bTest
这样的小写字母开头,所以在比较对应字段的时候就会出现不匹配的现象,导致结果为null
问题解决
采用最简单的json序列化方式,@JsonProperty
注解
@Data
public class HelloModel{
@JsonProperty("ATest")
private Strig ATest;
@JsonProperty("Btest")
private Strig Btest;
}
这样就可以完成序列化
1.3 json对象或json字符串与实体互转
1.3.1 实体转json对象或字符串
User u = new User ();
u.setUserId(123);
//转换为json对象
JSON jsonObject = JSON.toJSON(u);
//转换为json字符串
String jsonString = JSON.toJSONString(u);
得到的结果是一样的,只是类型不同而已
jsonObject:{"UserId":123}
jsonString:{"UserId":"123"}
1.3.2 json对象或json字符串转换为实体对象
//把json对象转换为实体对象
User userObject =JSON.toJavaObject(jsonObject,User.class);
//把json字符串转换为实体对象
User userString = JSON.parseObject(jsonString,User.class);
如果不想建立实体对象,而只想从JSON
对象中获取某一个字段可以这样操作(可以直接强转为Map
):
//解析字符串
//第一个参数是json字符串,第二个参数是实体类
JSONObject json =JSON.parseObject(JSONObejct.toJSONString(u));
String userId= (String)json.get("UserId");
System.out.println(userId);
注意:实体bean里面需要重写写toString
方法才能直接输出对象里面的每个属性。
1.4 Gson来解析和反解析json对象
1.4.1 解析普通对象
Gson
是由Google提供的效率功能更强大
User u = new User ();
u.setUserId(123);
Gson gson = new Gson();
//把对象转换为json字符串
String userString = gson.toJson(u);
System.out.println(userString);
//把json字符串转换为实体bean对象
User user = gson.fromJson(userString,User.class);
System.out.println(user);
得到的结果:
{"userId":123}
User [userId=123]
1.4.2 解析泛型对象
有如下泛型对象:
@Data
public class PageList<T> {
public List<T> body;
}
注意
:必须引用构建谷歌的TypeToken
对象
Gson gson = new Gson();
Type type = new TypeToken<PageList<User>>() {}.getType();
//此处的result是要解析的字符串
PageList<User> pageList = gson.fromJson(result, type);
1.5 JSONObject来解析和反解析
注意
:如果用的是阿里的json
类的话,JSONObject
是JSON
的子类
1.5.1 转为json对象或json字符串
User u = new User ();
u.setUserId(123);
//json对象
System.out.println(JSONObejct.toJSON(u));
//json字符串
System.out.println(JSONObejct.toJSONString(u));
得到的结果:
jsonObject:{"UserId":123}
jsonString:{"UserId":"123"}
1.5.2 反解析实体对象
1.5.2.1 反解析普通实体对象
//把json对象转换为实体对象
//第一个参数是JSON对象,第二个对象是要解析的实体类
User userObject =JSON.toJavaObject((JSON)JSONObejct.toJSON(u),User.class);
System.out.println(userObject);
//得到的结果
User{UserId=123}
//解析字符串
//第一个参数是json字符串,第二个参数是实体类
User userObject2 =JSON.parseObject(JSONObejct.toJSONString(u),User.class);
System.out.println(userObject2);
//得到的结果
User{UserId=123}
如果不想建立实体对象,而只想从JSONObject
对象中获取某一个字段可以这样操作(可以直接从Map
中处理,JSONObject
实现了Map
接口):
//解析字符串
//第一个参数是json字符串,第二个参数是实体类
JSONObject json =JSONObejct.parseObject(JSONObejct.toJSONString(u));
String userId= (String)json.get("UserId");
System.out.println(userId);
1.5.2.2 反解析泛型实体对象
使用TypeReference
对象获得解析泛型对象
有如下泛型对象:
@Data
public class PageList<T> {
public List<T> body;
}
可以使用如下方法解析:
//此处的result是json字符串
Result<PageList<User> result = JSONObject.parseObject(result, new TypeReference<Result<PageList<User>>>() {});
1.6 JSON引用和重复引用
简单说,重复引用就是一个集合/对象中的多个元素/属性同时引用同一对象,循环引用就是集合/对象中的多个元素/属性存在相互引用导致循环
1.6.1 重复引用
List<User> list = new ArrayList<>();
User user= new User();
list.add(user);
list.add(user);
System.out.println(JSONObject.toJSONString(list));
//得到的结果
[{"userId":"12"},{"$ref":"$[0]"}]
对于这个问题可以关闭json
的引用检测:
把JSONObject.toJSONString(list)
里面添加SerializerFeature.DisableCircularReferenceDetect
,如:JSONObject.toJSONString(list,SerializerFeature.DisableCircularReferenceDetect)
1.6.2 循环引用
// 循环引用的特殊情况,自引用
Map<String,Object> map = new HashMap<>();
map.put("map",map);
//
// map1引用了map2,而map2又引用map1,导致循环引用
Map<String,Object> map1 = new HashMap<>();
Map<String,Object> map2 = new HashMap<>();
map1.put("map",map2);
map2.put("map",map1);
对于循环引用,不能关闭json
的引用检测,不然会报错java.lang.StackOverflowError
,对于循环引用,最好的解决办法还是养成良好编程习惯
1.7 过滤某些字符串
public class Person
{
private String name;
private String address;
private String sex;
//省略getset方法
}
如果想过滤address属性怎么办
在address属性的get方法上添加注解@JSONField(serialize = false)
,如下
@JSONField(serialize = false)
public String getAddress() {
return address;
}
就可在序列化时忽略该字段
2 Fastjson源码分析对象首字母问题
2.1 问题引入
使用FastJson
, 在输出下面一段Json
的时候出现此问题, 期望是大写但是fastJson
将值自动首字母变成小写了
{"code":0,"message":"","result":{"facts":{"ip":{"aCUN_ONE_MIN":0,"aCUN_TEN_MIN":0}},"level":0}}
2.2 源码分析
查询后发现fastjson
内部做Bean
转换时会使用到 com.alibaba.fastjson.util.TypeUtils
, 核心代码如下, 在类加载的时候会去读取环境变量 fastjson.compatibleWithJavaBean
, 找不到则使用默认值false
,将会导致首字母小写
public static boolean compatibleWithJavaBean = false;
static {
try {
String prop = System.getProperty("fastjson.compatibleWithJavaBean");
if ("true".equals(prop)) {
compatibleWithJavaBean = true;
} else if ("false".equals(prop)) {
compatibleWithJavaBean = false;
}
} catch (Throwable ex) {
// skip }
}
public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap, boolean sorted) {
String propertyName;
if (Character.isUpperCase(c3)) {
if (compatibleWithJavaBean) {
propertyName = Introspector.decapitalize(methodName.substring(3));
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
} else if (c3 == '_') {
propertyName = methodName.substring(4);
} else if (c3 == 'f') {
propertyName = methodName.substring(3);
} else {
continue;
}}
2.3 解决方法
解决方案:
- 如果项目由多个模块且为分布式部署, 则可考虑使用设置
System.property
- 一般只是极少数的代码出现此情况, 那么建议直接在单例
Service
初始化时, 在静态块中直接改变TypeUtils
的变量值, 如果用Spring
的话可以使用InitializingBean
进行处理
TypeUtils.compatibleWithJavaBean = true;
- 此变量是
public
的注意要在一个地方进行改动, 避免线程安全问题
3 JSON和XML区别
3.1 定义介绍
3.1.1 XML定义
扩展标记语言 (Extensible Markup Language, XML
) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据
、定义数据类型
,是一种允许用户对自己的标记语言进行定义的源语言。 XML
使用DTD(document type definition)
文档类型定义来组织数据;格式统一,跨平台和语言,早已成为业界公认的标准。
XML
是标准通用标记语言 (SGML
) 的子集,非常适合 Web
传输。XML
提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
3.1.2 JSON定义
JSON(JavaScript Object Notation)
一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。JSON
采用兼容性很高的、完全独立于语言文本格式,同时也具备类似于C语言的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)体系的行为。这些特性使JSON
成为理想的数据交换语言。
JSON
基于JavaScript Programming Language , Standard ECMA-262 3rd Edition - December 1999
的一个子集。
3.2 XML和JSON优缺点
3.2.1 XML的优缺点
XML
的优点
:
- 格式统一,符合标准;
- 容易与其他系统进行远程交互,数据共享比较方便。
XML
的缺点
:
- XML文件庞大,文件格式复杂,传输占带宽;
- 服务器端和客户端都需要花费大量代码来解析
XML
,导致服务器端和客户端代码变得异常复杂且不易维护; - 客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
- 服务器端和客户端解析
XML
花费较多的资源和时间。
3.2.2 JSON的优缺点
JSON
的优点:
- 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
- 易于解析,客户端
JavaScript
可以简单的通过eval()
进行JSON
数据的读取; - 支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
- 因为
JSON格式
能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
JSON
的缺点
- 没有
XML
格式这么推广的深入人心和使用广泛,没有XML
那么通用性; JSON
格式目前在Web Service
中推广还属于初级阶段。
3.3 XML和JSON的优缺点对比
- 可读性方面
JSON
和XML
的数据可读性基本相同,JSON
和XML
的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,XML
可读性较好些。 - 可扩展性方面
XML
天生有很好的扩展性,JSON
当然也有,没有什么是XML
能扩展,JSON
不能的。 - 编码难度方面
XML
有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON
的编码明显比XML
容易许多,即使不借助工具也能写出JSON
的代码,可是要写好XML
就不太容易了。 - 解码难度方面
XML
的解析得考虑子节点父节点,让人头昏眼花,而JSON
的解析难度几乎为0
。这一点XML
输的真是没话说。 - 流行度方面
XML
已经被业界广泛的使用,而JSON
才刚刚开始,但是在Ajax
这个特定的领域,未来的发展一定是XML
让位于JSON
。到时Ajax应该变成Ajaj(Asynchronous Javascript and JSON)了。 - 解析手段方面
JSON和XML同样拥有丰富的解析手段。 - 数据体积方面
JSON相对于XML来讲,数据的体积小,传递的速度更快些。 - 数据交互方面。
JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。 - 数据描述方面。
JSON对数据的描述性比XML较差。 - 传输速度方面。
JSON的速度要远远快于XML。
3.4 XML与JSON数据格式比较
3.4.1 关于轻量级和重量级
轻量级和重量级是相对来说的,那么XML
相对于JSON
的重量级
体现在哪呢?应该体现在解析上,XML目前设计了两种解析方式:DOM
和 SAX
DOM
是把一个数据交换格式XML
看成一个DOM
对象,需要把XML
文件整个读入内存,这一点上JSON
和XML
的原理是一样的,但是XML
要考虑父节点和子节点,这一点上JSON
的解析难度要小很多,因为JSON
构建于两种结构:key/value
,键值对的集合;值的有序集合,可理解为数组;SAX
不需要整个读入文档就可以对解析出的内容进行处理,是一种逐步解析的方法。程序也可以随时终止解析。这样,一个大的文档就可以逐步的、一点一点的展现出来,所以SAX
适合于大规模的解析。这一点,JSON
目前是做不到得。
所以,JSON和XML的轻/重量级的区别在于:
JSON
只提供整体解析方案,而这种方法只在解析较少的数据时才能起到良好的效果;XML
提供了对大规模数据的逐步解析方案,这种方案很适合于对大量数据的处理。
3.4.2 关于数据格式编码及解析难度
关于数据格式编码及解析难度:
- 在编码方面
虽然XML
和JSON
都有各自的编码工具,但是JSON
的编码要比XML
简单,即使不借助工具,也可以写出JSON
代码,但要写出好的XML
代码就有点困难;与XML
一样,JSON
也是基于文本的,且它们都使用Unicode
编码,且其与数据交换格式XML
一样具有可读性。
主观上来看,JSON
更为清晰且冗余更少些。JSON
网站提供了对JSON
语法的严格描述,只是描述较简短。从总体来看,XML
比较适合于标记文档,而JSON
却更适于进行数据交换处理。 - 在解析方面。
在普通的web
应用领域,开发者经常为XML
的解析伤脑筋,无论是服务器端生成或处理XML
,还是客户端用JavaScript
解析XML
,都常常导致复杂的代码,极低的开发效率。
实际上,对于大多数Web应用来说,他们根本不需要复杂的XML来传输数据,XML宣称的扩展性在此就很少具有优势,许多Ajax应用甚至直接返回HTML片段来构建动态Web页面。和返回XML并解析它相比,返回HTML片段大大降低了系统的复杂性,但同时缺少了一定的灵活性。同XML或 HTML片段相比,数据交换格式JSON
提供了更好的简单性和灵活性。在Web Serivice应用中,至少就目前来说XML仍有不可动摇的地位。
3.4.3 实例比较
XML
和JSON
都使用结构化方法来标记数据,下面来做一个简单的比较。
用XML表示中国部分省市数据如下:
<?xml version="1.0" encoding="utf-8" ?><country>
<name>中国</name>
<province>
<name>黑龙江</name>
<citys>
<city>哈尔滨</city>
<city>大庆</city>
</citys>
</province>
<province>
<name>广东</name>
<citys>
<city>广州</city>
<city>深圳</city>
<city>珠海</city>
</citys>
</province>
<province>
<name>台湾</name>
<citys>
<city>台北</city>
<city>高雄</city>
</citys>
</province>
<province>
<name>新疆</name>
<citys>
<city>乌鲁木齐</city>
</citys>
</province></country>
用JSON表示中国部分省市数据如下:
var country =
{
name: "中国",
provinces: [
{ name: "黑龙江", citys: { city: ["哈尔滨", "大庆"]} },
{ name: "广东", citys: { city: ["广州", "深圳", "珠海"]} },
{ name: "台湾", citys: { city: ["台北", "高雄"]} },
{ name: "新疆", citys: { city: ["乌鲁木齐"]} }
]
}
编码的可读性来说,XML
有明显的优势,毕竟人类的语言更贴近这样的说明结构。JSON
读起来更像一个数据块,读起来就比较费解了。不过,我们读起来费解的语言,恰恰是适合机器阅读,所以通过JSON
的索引country.provinces[0].name
就能够读取“黑龙江”这个值。
编码的手写难度来说,XML还是舒服一些,好读当然就好写。不过写出来的字符JSON就明显少很多。去掉空白制表以及换行的话,JSON就是密密麻麻的有用数据,而XML却包含很多重复的标记字符。