JavaにおけるオブジェクトJSONフォーマット処理時のピット
19005 ワード
プロジェクトでJSONのピットに遭遇しました.記録する.
直接コード:
出力された結果は次のとおりです.
しかし、私たちが期待している結果は、次のような結果です.
では、どこが問題なのでしょうか.MyListのsize属性とspecialNameがJSONでフォーマットされた場合、破棄されますか?
次に例を示します.
出力された結果は次のとおりです.
結果は完全に正しい.
ここまで来たら、私たちは何が原因なのか知ることができるはずです.
上記の最初のコードで期待される結果は、次のとおりです.
{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フォーマットでは、リストまたは配列として処理されます.関連するソースコードを見てみましょう.
我々のMyListのオブジェクトはif(javaObject instanceof Collection)でtrueであるためJSOnArrayとして扱われており、JSOnArrayの1つを扱うたびにsize属性とspecialName属性には扱われないことは明らかであるため、この2つの属性は破棄されている.これはJSONです.toJSON()の処理手順.
次はJSONを見てみましょう.toJSOnString()は、実は同じで、配列やリストとして処理されています.
上のコードelse if(List.class.isAssignableFrom(clazz))はtrueなので、本当はListSerializer.InstanceがMyListのオブジェクトを処理するのはjavaとする.util.リストが処理するので,size属性とspecialName属性は自然に捨てられる.上の原理と同じです.
まとめ:
1)MyListはこのようにListから継承され、拡張属性を有し、json形式では十分に適切に表現できず、Listとして処理され、他の非Listの属性を捨てる.
2)回避方法としては,継承の代わりに組合せを用いるか,Map,javaを用いる.util.Mapは生まれつきjson形式と最も適切で、実はjavascriptの中のオブジェクト、実はある程度1つのMapで、属性名はmapの中のkeyで、属性の値はmapのvalueです.
3)JSONフォーマットの紹介参照:http://www.cnblogs.com/digdeep/p/4572662.html
直接コード:
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フォーマットでは、リストまたは配列として処理されます.関連するソースコードを見てみましょう.
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の1つを扱うたびにsize属性とspecialName属性には扱われないことは明らかであるため、この2つの属性は破棄されている.これはJSONです.toJSON()の処理手順.
次はJSONを見てみましょう.toJSOnString()は、実は同じで、配列やリストとして処理されています.
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.リストが処理するので,size属性とspecialName属性は自然に捨てられる.上の原理と同じです.
まとめ:
1)MyListはこのようにListから継承され、拡張属性を有し、json形式では十分に適切に表現できず、Listとして処理され、他の非Listの属性を捨てる.
2)回避方法としては,継承の代わりに組合せを用いるか,Map,javaを用いる.util.Mapは生まれつきjson形式と最も適切で、実はjavascriptの中のオブジェクト、実はある程度1つのMapで、属性名はmapの中のkeyで、属性の値はmapのvalueです.
3)JSONフォーマットの紹介参照:http://www.cnblogs.com/digdeep/p/4572662.html