Java中对象JSON格式化处理时的⼀个坑在项⽬中遇到了⼀个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.JSON(list));
System.out.JSONString(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;
object to}
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.JSON(myList));
System.out.JSONString(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.JSON(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, GlobalInstance());
}
@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 : Set()) {
Object key = Key();
String jsonKey = TypeUtils.castToString(key);
Object jsonValue = Value());
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() 的处理过程。
下⾯看下 JSONString(),其实也是⼀样,当成了数组或者说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 : AutowiredFor()) {
put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
}
writer = get(clazz);
}
if (writer == null) {
final ClassLoader classLoader = ClassLoader();
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 : AutowiredFor()) {
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。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。