JSON字符串和对象(泛型)互转以及和XML区别

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 类时,会出现 ATestBTest 都为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类的话,JSONObjectJSON的子类

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 解决方法

解决方案:

  1. 如果项目由多个模块且为分布式部署, 则可考虑使用设置System.property
  2. 一般只是极少数的代码出现此情况, 那么建议直接在单例Service初始化时, 在静态块中直接改变TypeUtils的变量值, 如果用Spring的话可以使用InitializingBean进行处理
TypeUtils.compatibleWithJavaBean = true;
  1. 此变量是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的优缺点对比

  1. 可读性方面
    JSONXML的数据可读性基本相同,JSONXML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,XML可读性较好些。
  2. 可扩展性方面
    XML天生有很好的扩展性,JSON当然也有,没有什么是XML能扩展,JSON不能的。
  3. 编码难度方面
    XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。
  4. 解码难度方面
    XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。
  5. 流行度方面
    XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON。到时Ajax应该变成Ajaj(Asynchronous Javascript and JSON)了。
  6. 解析手段方面
    JSON和XML同样拥有丰富的解析手段。
  7. 数据体积方面
    JSON相对于XML来讲,数据的体积小,传递的速度更快些。
  8. 数据交互方面。
    JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
  9. 数据描述方面。
    JSON对数据的描述性比XML较差。
  10. 传输速度方面。
    JSON的速度要远远快于XML。

3.4 XML与JSON数据格式比较

3.4.1 关于轻量级和重量级

轻量级和重量级是相对来说的,那么XML相对于JSON重量级体现在哪呢?应该体现在解析上,XML目前设计了两种解析方式:DOMSAX

  • DOM是把一个数据交换格式XML看成一个DOM对象,需要把XML文件整个读入内存,这一点上JSONXML的原理是一样的,但是XML要考虑父节点和子节点,这一点上JSON的解析难度要小很多,因为JSON构建于两种结构:key/value,键值对的集合;值的有序集合,可理解为数组;
  • SAX不需要整个读入文档就可以对解析出的内容进行处理,是一种逐步解析的方法。程序也可以随时终止解析。这样,一个大的文档就可以逐步的、一点一点的展现出来,所以SAX适合于大规模的解析。这一点,JSON目前是做不到得。

所以,JSON和XML的轻/重量级的区别在于:

  • JSON只提供整体解析方案,而这种方法只在解析较少的数据时才能起到良好的效果;
  • XML提供了对大规模数据的逐步解析方案,这种方案很适合于对大量数据的处理。

3.4.2 关于数据格式编码及解析难度

关于数据格式编码及解析难度:

  1. 在编码方面
    虽然XMLJSON都有各自的编码工具,但是JSON的编码要比XML简单,即使不借助工具,也可以写出JSON代码,但要写出好的XML代码就有点困难;与XML一样,JSON也是基于文本的,且它们都使用Unicode编码,且其与数据交换格式XML一样具有可读性。
    主观上来看,JSON更为清晰且冗余更少些。JSON网站提供了对JSON语法的严格描述,只是描述较简短。从总体来看,XML比较适合于标记文档,而JSON却更适于进行数据交换处理。
  2. 在解析方面。
    在普通的web应用领域,开发者经常为XML的解析伤脑筋,无论是服务器端生成或处理XML,还是客户端用 JavaScript 解析XML,都常常导致复杂的代码,极低的开发效率。
    实际上,对于大多数Web应用来说,他们根本不需要复杂的XML来传输数据,XML宣称的扩展性在此就很少具有优势,许多Ajax应用甚至直接返回HTML片段来构建动态Web页面。和返回XML并解析它相比,返回HTML片段大大降低了系统的复杂性,但同时缺少了一定的灵活性。同XML或 HTML片段相比,数据交换格式JSON 提供了更好的简单性和灵活性。在Web Serivice应用中,至少就目前来说XML仍有不可动摇的地位。

3.4.3 实例比较

XMLJSON都使用结构化方法来标记数据,下面来做一个简单的比较。

用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却包含很多重复的标记字符。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值