处理JSON循环引用序列化与反序列化问题的终极方案

摘要:
对于服务器端,我相信大多数人遇到的问题是输出JSON序列化数据。首先让我们来谈谈一些基本问题:对于前端,JSON通常需要与服务器部分和本地存储进行通信。对于后端,我认为最常见的问题是串行化。Basicthingsdone公司。循环引用对象序列化?后端相关文章必须告诉您“禁用循环引用检测”、“截断对象”、“配置序列化规则”以及其他与魔术方法相关的前端文章。最多可以设置序列化深度。因此,会导致数据丢失,并将大量时间花费在序列化和反序列化逻辑的判断和处理上,从而导致许多难以维护的错误和浪费社会资源。首先,编写一个序列化和反序列化方法:SerializationCircular?

  重要声明:此博借鉴了阿里巴巴 Fastjson 的一点思想

  The important declaration: This mind comes form Fastjson Lib (Alibaba).

  说点『科(fei)普(hua)』先:

  1. 对于web前端,JSON序列化可以说是在 与服务端通讯(ajax+json) ,和使用 localStorage(读 + 写) 时。
  2. 对于服务端,我相信绝大多数人遇到问题是在于输出JSON序列化数据。

  『科(fei)普(hua)』完毕。

  Let's talk about some basic things first:

  1. For the web front-end, JSON is usually used on conmunication with server part, and localStorage.
  2. For the back-end, I think where the most people have problem is serialization.

  Basic things done.

  循环引用对象序列化?这似乎是一个老生常谈的问题,但是99.9%的人所谓的『解决』,都是在『逃避』这个问题,不信你搜搜『循环引用 JSON』试试?
  后端相关文章一定告诉你要『禁用循环引用检测』,『截断对象』和『配置序列化规则( 各种Filter )』等降( tao )魔( bi )大法

  前端相关文章最多就是告诉你可以设置一下序列化深度。
  于是导致了数据丢失,花大量时间在序列化和反序列化逻辑上做判断和处理,出现许多难以维护的bug,浪费社会资源。
  说到底,JSON不就是数据交换吗?它不支持表示循环引用对象,那就对它的语法进行扩展,让它能够表示不就好了?迎刃而解。
  如何表示循环引用/重复引用对象?阿里的Fastjson已经告诉了我们答案:

  1. 创建一个对象表示引用对象,它仅有一个 key="$ref",value=对象引用路径
  2. 对象引用路径使用 "$" 表示根对象的引用,使用 "[数字]" 表示数组元素,使用 ".property" 表示对象字段
  3. 形如{ "$ref":"$.key1.array[3].key" }

  Fastjson 是 Java 的库,服务于后端,我在这里用 TypeScript 手写一下它的实现以便前端能够享受这个人类宝贵的精神财富。

  首先写个序列化和反序列化方法:( serializeCircular 和 parseCircular )

  Serialization Circular? It seems to be a very familiar issue? But what 99.9% of people call "handle" is "escape".Or you can try searching "Circular JSON".

  The information about back-end would tell you to "disable circular checking", "cut object" and "create filters" to "h( e )a( s )n( c )d( a )l( p )e" it.

  The information about front-end would tell you to change/set the deep/level in the settings of serialization.

  And all above would cause losing data, and you will take huge time at the further logics of serialization, fixing bugs. It's a sheer waste.

  But all in all, JSON is just for data-exchanging. It doesn't support circular , why not expand the role to make it supports? 

  How to express circular or repeatition? The answer has already been in the Fastjson lib, which built by Alibaba:

  1. Create an object to express the circular, which has only one property named "$ref" and the value of it is a path-expression, which express the position of the circular from the root.
  2. It uses "$" to express the reference of the root, "[index:number]" to express the item of an array, and ".key" to express the key of an common object.
  3. Just like { "$ref":"$.key1.array[3].key" }

  But Fastjson is a Java Lab for back-end, so I implement it by TypeScript for front-end.

  At the first, make serialization and parse function: ( serializeCircular and parseCircular )

 1 const _parseCircular = (root: any, parent: any, objkey: string | number) => {
 2   const obj = parent[objkey];
 3   if (null === obj || typeof obj !== "object") {
 4     //
 5   } else if (Array.isArray(obj)) {
 6     for (let i = 0; i < obj.length; i++) {
 7       _parseCircular(root, obj, i);
 8     }
 9   } else if (!!obj["$ref"]) {
10     let paths = (obj["$ref"] as string).split(/.|[|]/).filter(s => !!s);
11     paths.shift();
12     parent[objkey] = paths.reduce((a, b) => a[b], root);
13   } else {
14     Object.keys(obj).forEach(key => {
15       _parseCircular(root, obj, key);
16     });
17   }
18 };
19 const _serializeCircular = (parent: any, base: string, objkey: string | number, obj_key_map: Map<string, any>, result: any) => {
20   const obj = parent[objkey];
21   if (null === obj || typeof obj !== "object") {
22     result[objkey] = obj;
23   } else if (obj_key_map.has(obj)) {
24     result[objkey] = { $ref: obj_key_map.get(obj) };
25   } else {
26     const endFix = Array.isArray(parent) ? `[${objkey}]` : `.${objkey}`;
27     let objrefstr = `${base}${endFix}`;
28     obj_key_map.set(obj, objrefstr);
29     if (Array.isArray(obj)) {
30       result = result[objkey] = [];
31       for (let i = 0; i < obj.length; i++) {
32         _serializeCircular(obj, objrefstr, i, obj_key_map, result);
33       }
34     } else {
35       result = result[objkey] = {};
36       Object.keys(obj).forEach(key => {
37         _serializeCircular(obj, objrefstr, key, obj_key_map, result);
38       });
39     }
40   }
41 };
42 const serializeCircular = (root: any) => {
43   const map = new Map();
44   map.set(root, "$");
45   if (Array.isArray(root)) {
46     let result = [];
47     for (let i = 0; i < root.length; i++) {
48       _serializeCircular(root, "$", i, map, result);
49     }
50     return result;
51   } else if (null !== root && typeof root === "object") {
52     let result = {};
53     Object.keys(root).forEach(key => {
54       _serializeCircular(root, "$", key, map, result);
55     });
56     return result;
57   } else {
58     return root;
59   }
60 };
61 const parseCircular = (root: any): any => {
62   if (Array.isArray(root)) {
63     for (let i = 0; i < root.length; i++) {
64       _parseCircular(root, root, i);
65     }
66   } else if (null !== root && typeof root === "object") {
67     Object.keys(root).forEach(key => {
68       _parseCircular(root, root, key);
69     });
70   }
71   return root;
72 };

  然后你可以仅仅只是用在某些特定的地方 ( 推荐 ),如 RPC 和 localStorage

  或者直接替换掉原本的 JSON.stringify 和 JSON.parse ( 也推荐 ) ^_^ 

  Then you can just use it at some special places. ( recommend ) such as RPC and localStorage

  Or just replace the original JSON.stringify and JSON.parse.( recommend too ) ^_^

 1 let stringifyInited = false;
 2 if (!stringifyInited && (stringifyInited = true)) {
 3   const oriStringify = JSON.stringify;
 4   const oriParse = JSON.parse;
 5   const serialize = serializeCircular;
 6   const parse = parseCircular;
 7   JSON.stringify = function (this: any) {
 8     const args = Array.from(arguments) as any;
 9     args[0] = serialize(args[0]);
10     return oriStringify.apply(this, args);
11   } as any;
12   JSON.parse = function (this: any) {
13     const args = Array.from(arguments) as any;
14     let res = oriParse.apply(this, args);
15     return parse(res);
16   } as any;
17 }

  测试:

  Test:

 1 // 测试 test
 2 
 3 (() => {
 4     let abc = {} as any;
 5     abc.a = {};
 6     abc.b = {};
 7     abc.a.b = abc.b;
 8     abc.b.a = abc.a;
 9     let abcSerialization = JSON.stringify(abc);
10     console.log(abcSerialization);
11     let abcParse = JSON.parse(abcSerialization);
12     console.log(abcParse.a===abcParse.b.a);
13 })();
14 
15 // {"a":{"b":{"a":{"$ref":"$.a"}}},"b":{"$ref":"$.a.b"}}
16 // true

 

  最后,需要 Javascript 版本的朋友们,只需把类型限定标记 ( : ??? 和 as ??? ) 去掉。

  感谢阅读

  At the end, my friends, if need a JavaScript version, just remove the limitation marks ( : ??? 和 as ??? )

  Thanks for reading

 

免责声明:文章转载自《处理JSON循环引用序列化与反序列化问题的终极方案》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mac 电脑如何安装mac os 和win7双系统(win7多分区)ios基础笔记(一)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于序列化:把某个对象序列化成字节流

在网络编程中。一个常常的操作是将本地的数据块转换成字符流并将其发送到远端。远端将这个字符串流恢复成数据库。如果有例如以下一个类CObject。编写两个函数,分别将CObject中的成员变量转换为一个字符流(convert2Stream()函数)。并将字符流的数据又一次恢复到一个CObject对象中(convert2Object()函数): char* c...

Newtonsoft.Json 的基本用法

Ø  前言 说起 C# 对 JSON 的操作(序列化与反序列化),大家都会想到 JavaScriptSerializer、DataContractJsonSerializer 与 Newtonsoft.Json 等。三者都是用于操作 JSON 的框架利器,它们又有什么区别呢?本文包括: 1.   常用 JSON 操作框架(JavaScriptSeriali...

java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)

切面打印日志时,参数序列化异常异常信息:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)原因jo...

Redis的序列化

本文参考http://www.cnblogs.com/yaobolove/p/5632891.html Redis通过序列化存对象。 首先来了解为什么实现序列化接口?     当一个类实现了Serializable接口(该接口仅标记为接口,不包含任何方法定义),表示该类可以序列化。序列化的目的是将一个实现了Serializable接口的对象转化成一个字节序...

【Java基础】序列化与反序列化深入分析

一、前言   复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记。 二、为什么需要序列化与反序列化   程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终止,那么对象是肯定不会存在的,但是有时候,我们需要再程序终止时保存对象的状态信息,之后程序再次运行时可以重新恢复到之前的状态,如,玩家玩游戏退出时,需...

11-14序列化模块之json、pickle、shelve

序列化的目的 1、以某种存储形式使自定义对象持久化; 2、将对象从一个地方传递到另一个地方。 3、使程序更具维护性。 序列化--转向一个字符串数据类型序列--及时字符串何处用到:  数据存储  网络上传输的时候从数据类型到字符串的过程,叫做序列化从字符串到数据类型的过程,叫做反序列化分类 json ***** pickle ****...