在项目中遇到了一个JSON的坑。记录下。

直接上代码:

import java.util.ArrayList;

import com.alibaba.fastjson.JSON;

public class MyList<E> extends ArrayList<E> {
private int size;
private String specialName; public MyList(){
super(0);
} public MyList(int size){
super(0);
this.size = size;
}
public MyList(String specialName){
super(0);
this.specialName = specialName;
}
public MyList(int size, String specialName){
super(0);
this.size = size;
this.specialName = specialName;
}
public int getSize() {
return size;
} public void setSize(int size) {
this.size = size;
} public String getSpecialName() {
return specialName;
} public void setSpecialName(String specialName) {
this.specialName = specialName;
} public static void main(String[] args){
MyList<Integer> list = new MyList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.setSpecialName("just a test");
list.setSize(4);
System.out.println(JSON.toJSON(list));
System.out.println(JSON.toJSONString(list));
}
}

输出的结果为:

[1,2,3,4]
[1,2,3,4]

但是我们期望的结果却是类似于下面这样的结果:

{size:4, specialName:"just a test", [1,2,3,4]}

那么是哪里出问题了呢?导致 MyList的 size 属性和 specialName 在JSON格式化时,被丢弃了呢

下面在看一个例子:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import com.alibaba.fastjson.JSON; public class MyList2<E> {
private int size;
private String specialName;
private List<E> list; public MyList2(){ } public int getSize() {
return size;
} public void setSize(int size) {
this.size = size;
} public String getSpecialName() {
return specialName;
} public void setSpecialName(String specialName) {
this.specialName = specialName;
} public List<E> getList() {
return list;
} public void setList(List<E> list) {
this.list = list;
} public static void main(String[] args){
MyList2<Integer> myList = new MyList2<Integer>();
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4); myList.setSpecialName("just a test");
myList.setSize(4);
myList.setList(list);
System.out.println(JSON.toJSON(myList));
System.out.println(JSON.toJSONString(myList)); System.out.println("----------------"); Map<String, Object> map = new HashMap<>();
map.put("size", 4);
map.put("specialName", "just a test");
map.put("list", list);
map.put("myList", myList);
System.out.println(JSON.toJSON(map));
}
}

输出的结果为:

{"list":[1,2,3,4],"size":4,"specialName":"just a test"}
{"list":[1,2,3,4],"size":4,"specialName":"just a test"}
----------------
{"specialName":"just a test","size":4,"list":[1,2,3,4],"myList":{"list":[1,2,3,4],"size":4,"specialName":"just a test"}}

结果完全正确。

到这里,我们应该可以知道什么原因了。

上面第一段代码我们期望的结果:

{size:4, specialName:"just a test", [1,2,3,4]}

但是想一想,这个格式有什么问题吗?仔细想一想

FK,这个格式是完全错误的!他是不合法的。试想一下,JSON格式说白了,它是 Javascript 的一个子集,合法的json对象它是 javascript 中的对象,但是:

{size:4, specialName:"just a test", [1,2,3,4]}

他是一个合法的javascript对象吗????

显然,它不是的。因为其中的 [1,2,3,4] ,我们无法访问,它没有对应到一个属性所以我们无法访问它。所以他不是一个合法的javascript对象,显然也就更加不是一个合法的json对象了。所以第一个例子,输出的结果:[1,2,3,4] 其实是可以接受的。

是我们自己想要将一个不适合转化成JSON格式的Java对象强制转化成一个json格式,所以也就难免出现意料之外的情况了。那么 [1,2,3,4] 这个结果是如何来的呢。经过调试,其中的原因是,因为MyList继承自ArrayList,所以在JSON格式化时,就是将其作为了一个List或者说数组来处理的,我们看下相关源码:

    public static final Object toJSON(Object javaObject) {
return toJSON(javaObject, ParserConfig.getGlobalInstance());
} @SuppressWarnings("unchecked")
public static final Object toJSON(Object javaObject, ParserConfig mapping) {
if (javaObject == null) {
return null;
} if (javaObject instanceof JSON) {
return (JSON) javaObject;
} if (javaObject instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) javaObject; JSONObject json = new JSONObject(map.size()); for (Map.Entry<Object, Object> entry : map.entrySet()) {
Object key = entry.getKey();
String jsonKey = TypeUtils.castToString(key);
Object jsonValue = toJSON(entry.getValue());
json.put(jsonKey, jsonValue);
} return json;
} if (javaObject instanceof Collection) {
Collection<Object> collection = (Collection<Object>) javaObject; JSONArray array = new JSONArray(collection.size()); for (Object item : collection) {
Object jsonValue = toJSON(item);
array.add(jsonValue);
} return array;
}

我们的MyList的对象,在 if (javaObject instanceof Collection) 处为true,所以被当做了JSONArray 来处理的,每次处理JSONArray 中的一项,所以显然就不会处理到 size属性和specialName属性了,所以这两个属性被抛弃了。这是 JSON.toJSON() 的处理过程。

下面看下 JSON.toJSONString(),其实也是一样,当成了数组或者说List来处理了:

    public ObjectSerializer getObjectWriter(Class<?> clazz) {
ObjectSerializer writer = get(clazz); if (writer == null) {
try {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
if (!(o instanceof AutowiredObjectSerializer)) {
continue;
} AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
for (Type forType : autowired.getAutowiredFor()) {
put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
} writer = get(clazz);
} if (writer == null) {
final ClassLoader classLoader = JSON.class.getClassLoader();
if (classLoader != Thread.currentThread().getContextClassLoader()) {
try {
for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) { if (!(o instanceof AutowiredObjectSerializer)) {
continue;
} AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
for (Type forType : autowired.getAutowiredFor()) {
put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
} writer = get(clazz);
}
} if (writer == null) {
if (Map.class.isAssignableFrom(clazz)) {
put(clazz, MapSerializer.instance);
} else if (List.class.isAssignableFrom(clazz)) {
put(clazz, ListSerializer.instance);

上面的代码 else if (List.class.isAssignableFrom(clazz)) 处为 true,所以其实是使用了 ListSerializer.instance 来处理了 MyList 的对象的,也就是当做了java.util.List 来处理的,所以自然就抛弃了 size属性和specialName属性。和上面的原理是一样的。

总结下

1)MyList这样继承自List的对象,并且有扩展属性的,它在json格式中是不能十分恰当的表示的,它会被当成List来处理,从而抛弃其它非List的属性。

2)避免方法就是使用组合代替继承,或者使用Map,java.util.Map 天生就和json格式是最合适的,其实javascript中的对象,其实在某种程度上就是一个Map而已,属性名就是map中的key, 属性的值就是map的value。

3)JSON格式介绍参见:http://www.cnblogs.com/digdeep/p/4572662.html

Java中对象JSON格式化处理时的一个坑的更多相关文章

  1. Windows下Java File对象创建文件夹时的一个"坑"

    import java.io.File; import java.io.IOException; public class DirCreate { public static void main(St ...

  2. java中变量命名和引用变量的一个坑

    这次有两个主题,第一个太简单啦,就是java中变量的命名规则,纯记忆性东西.第二个主题,就是讨论一下对象引用变量的一个注意点.

  3. Java中哪个JSON库的解析速度是最快的?

    JSON已经成为当前服务器与WEB应用之间数据传输的公认标准,不过正如许多我们所习以为常的事情一样,你会觉得这是理所当然的便不再深入思考 了.我们很少会去想用到的这些JSON库到底有什么不同,但事实上 ...

  4. JAVA中使用JSON进行数据传递

    最近在做一个基于JAVA Servlet的WEB应用以及对应的Anroid应用客户端的开发工作. 其中,在接口的访问和数据的传输方面使用的比较多的是使用JSON对象来操作格式化数据:在服务器端采用JS ...

  5. Java中对象的深复制和浅复制详解

    1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...

  6. 转载:JAVA中使用JSON进行数据传递

    转载网址:http://www.cnblogs.com/undead/archive/2012/07/18/2594900.html 最近在做一个基于JAVA Servlet的WEB应用以及对应的An ...

  7. JSON(三)——java中对于JSON格式数据的解析之json-lib与jackson

    java中对于JSON格式数据的操作,主要是json格式字符串与JavaBean之间的相互转换.java中能够解析JSON格式数据的框架有很多,比如json-lib,jackson,阿里巴巴的fast ...

  8. 谈谈java中对象的深拷贝与浅拷贝

    知识点:java中关于Object.clone方法,对象的深拷贝与浅拷贝 引言: 在一些场景中,我们需要获取到一个对象的拷贝,这时候就可以用java中的Object.clone方法进行对象的复制,得到 ...

  9. Java中对象、对象引用、堆、栈、值传递以及引用传递的详解

    Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...

随机推荐

  1. Vim 的光标移动定位

    一.光标移动以单个字符为单位: 在命令模式中 h向左 l 向右 j 向上 k 向下 二.光标移动以word 为单位: w 将光标向前移动一个word; b 将光标向后移动一个word: 以上2个命令光 ...

  2. HDU 2896 病毒侵袭(AC自动机)题解

    题意:给你n个模式串,再给你m个主串,问你每个主串中有多少模式串,并输出是哪些.注意一下,这里给的字符范围是可见字符0~127,所以要开130左右. 思路:用字典树开的时候储存编号,匹配完成后set记 ...

  3. 如何在 OSX 中使用多个JDK版本

    升级macbook小白的硬盘成SSD后,重新安装了系统和JDK8,但是启动eclipse还是报告需要安装JDK6,于是也按照提示安装了Apple JDK6,这导致系统中有两个JDK,一个是Oracle ...

  4. 谈谈java中对象的深拷贝与浅拷贝

    知识点:java中关于Object.clone方法,对象的深拷贝与浅拷贝 引言: 在一些场景中,我们需要获取到一个对象的拷贝,这时候就可以用java中的Object.clone方法进行对象的复制,得到 ...

  5. SDN原理 OpenFlow协议 -3

    问题4:流表匹配 OF1.1版本 这是OF1.1版本的操作,引入了多流表,1.0版本并没有多流表. 多流表的匹配称为 流水线处理:交换机从流表0开始查找,按照流表序号从小到大匹配. 每个包按照优先级去 ...

  6. SDN原理 OpenFlow协议 -1

    本文基于SDN原理视频而成:SDN原理 OpenFlow OpenFlow 协议 和 传统的路由选择协议 有很多相似的地方,同时在某些地方也具有一定的颠覆性. 路由表,由IP地址和子网掩码组成.MAC ...

  7. [原][osgearth]API加载earth文件的解析

    参考:http://blog.csdn.net/cccstudyer/article/details/17691893 通过\src\osgEarthDrivers\earth\ReaderWrite ...

  8. RHEL6.5恢复root密码

    1.开机上下键停留在如下界面,键盘输入小写e: 2.选择如下选项,并输入小写e: 3.输入1,回车进入单用户模式: 4.键盘输入小写b,进行启动: 5.进入到单用户模式: 6.修改root用户密码,并 ...

  9. BooStrap4文档摘录 2 Content, Component

    Content Reboot:从新写了主要元素的排列. 本章讲了各种元素及其相关的类. ⚠️ 文档左上角有搜索栏. Components Alert✅ Badge✅ Button✅和Button gr ...

  10. TCP握手及状态图

    为什么需要“三次握手”? 为了解决“网络中存在延迟的重复分组”的问题,即为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误. 例:“已失效的连接请求报文段”的产生在这样一种情况下:cli ...