一个 json 转换工具
在前后端的数据协议(主要指http和websocket)的问题上,如果前期沟通好了,那么数据协议上问题会很好解决,前后端商议一种都可以接受的格式即可。但是如果接入的是老系统、第三方系统,或者由于某些奇怪的需求(如为了节省流量,json 数据使用单字母作为key值,或者对某一段数据进行了加密),这些情况下就无法商议,需要在前端做数据转换,如果不转换,那么奔放的数据格式可读性差,也会造成项目难以维护。
这也正是我在项目种遇到的问题,网上也找了一些方案,要么过于复杂,要么有些功能不能很好的支持,于是有了这个工具[class-converter](https://www.npmjs.com/package/class-converter)。
下面我们用例子来说明下:
面对如下的Server返回的一个用户user数据:
{
"i": 1234,
"n": "name",
"a": "1a2b3c4d5e6f7a8b"
}
或者这个样的:
{
"user_id": 1234,
"user_name": "name",
"u_avatar": "1a2b3c4d5e6f7a8b"
}
数据里的 avatar 字段在使用时,可能需要拼接成一个 url,例如 https://xxx.cdn.com/1a2b3c4d5e6f7a8b.png。
当然可以直接这么做:
const json = {
"i": 1234,
"n": "name",
"a": "1a2b3c4d5e6f7a8b",
};
const data = {};
const keyMap = {
i: 'id',
n: 'name',
a: 'avatar',
}
Object.entries(json).forEach(([key, value]) => {
data[keyMap[key]] = value;
});
// data = { id: 1234, name: 'name', avatar: '1a2b3c4d5e6f7a8b' }
然后我们进一步就可以把这个抽象成一个方法,像下面这个样:
const jsonConverter = (json, keyMap) => {
const data = {};
Object.entries(json).forEach(([key, value]) => {
data[keyMap[key]] = value;
});
return data;
}
如果这个数据扩展了,添加了教育信息,user 数据结构看起来这个样:
{
"i": 1234,
"n": "name",
"a": "1a2b3c4d5e6f7a8b",
"edu": {
"u": "South China Normal University",
"ea": 1
}
}
此时的 jsonConverter 方法已经无法正确转换 edu 字段的数据,需要做一些修改:
const json = {
"i": 1234,
"n": "name",
"a": "1a2b3c4d5e6f7a8b",
"edu": {
"u": "South China Normal University",
"ea": 1
}
};
const data = {};
const keyMap = {
i: 'id',
n: 'name',
a: 'avatar',
edu: {
key: 'education',
keyMap: {
u: 'universityName',
ea: 'attainment'
}
},
}
随着数据复杂度的上升,keyMap 数据结构会变成一个臃肿的配置文件,此外 jsonConverter 方法会越来越复杂,以至于后面同样难以维护。但是转换后的数据格式,对于项目来说,数据的可读性是很高的。所以,这个转换必须做,但是方式可以更优雅一点。
写这个工具的初衷也是为了更优雅的进行数据转换。
工具用法
还是上面的例子(这里使用typescript写法):
import { toClass, property } from 'class-converter';
// 待解析的数据
const json = {
"i": 1234,
"n": "name",
"a": "1a2b3c4d5e6f7a8b",
};
class User {
@property('i')
id: number;
@property('n')
name: string;
@property('a')
avatar: string;
}
const userIns = toClass(json, User);
你可以轻而易举的获得下面的数据:
// userIns 是 User 的一个实例
const userIns = {
id: 1234,
name: 'name',
avatar: '1a2b3c4d5e6f7a8b',
}
userIns instanceof User // true
Json 类既是文档又是类似于上文说的与keyMap类似的配置文件,并且可以反向使用。
import { toPlain } from 'class-converter';
const user = toPlain(userIns, User);
// user 数据结构
{
i: 1234,
n: 'name',
a: '1a2b3c4d5e6f7a8b',
};
这是一个最简单的例子,我们来一个复杂的数据结构:
{
"i": 10000,
"n": "name",
"user": {
"i": 20000,
"n": "name1",
"email": "zqczqc",
// {"i":1111,"n":"department"}
"d": "eyJpIjoxMTExLCJuIjoiZGVwYXJ0bWVudCJ9",
"edu": [
{
"i": 1111,
"sn": "szzx"
},
{
"i": 2222,
"sn": "scnu"
},
{
"i": 3333
}
]
}
}
这是后端返回的一个叫package的json对象,字段意义在文档中这么解释:
- i:package 的 id
- n:package 的名字
- user:package 的所有者,一个用户
- i:用户 id
- n:用户名称
- email:用户email,但是只有邮箱前缀
- d:用户的所在部门,使用了base64编码了一个json字符串
- i:部门 id
- n:部门名称
- edu:用户的教育信息,数组格式
- i:学校 id
- sn:学校名称
我们的期望是将这一段数据解析成,不看文档也能读懂的一个json对象,首先我们经过分析得出上面一共有4类实体对象:package、用户信息、部门信息、教育信息。
下面是代码实现:
import {
toClass, property, array, defaultVal,
beforeDeserialize, deserialize, optional
} from 'class-converter';
// 教育信息
class Education {
@property('i')
id: number;
// 提供一个默认值
@defaultVal('unknow')
@prperty('sn')
schoolName: string;
}
// 部门信息
class Department {
@property('i')
id: number;
@prperty('n')
name: string;
}
// 用户信息
class User {
@property('i')
id: number;
@property('n')
name: string;
// 保留一份邮箱前缀数据
@optional()
@property()
emailPrefix: string;
@optional()
// 这里希望自动把后缀加上去
@deserialize(val => `${val}@xxx.com`)
@property()
email: string;
@beforeDeserialize(val => JSON.parse(atob(val)))
@typed(Department)
@property('d')
department: Department;
@array()
@typed(Education)
@property('edu')
educations: Education[];
}
// package
class Package {
@property('i')
id: number;
@property('n')
name: string;
@property('user', User)
owner: User;
}
数据已经定义完毕,这时只要我们执行toClass方法就可以得到我们想要的数据格式:
{
id: 10000,
name: 'name',
owner: {
id: 20000,
name: 'name1',
emailPrefix: 'zqczqc',
email: "zqczqc@xxx.com",
department: {
id: 1111,
name: 'department'
},
educations: [
{
id: 1111,
schoolName: 'szzx'
},
{
id: 2222,
schoolName: 'scnu'
},
{
id: 3333,
schoolName: 'unknow'
}
]
}
}
上面这一份数据,相比后端返回的数据格式,可读性大大提升。这里的用法出现了@deserialize、@beforeDeserialize、@yped的装饰器,这里对这几个装饰器是管道方式调用的(前一个的输出一个的输入),这里做一个解释:
beforeDeserialize第一个参数可以最早拿到当前属性值,这里可以做一些解码操作typed这个是转换的类型,入参是一个类,相当于自动调用toClass,并且调动时的第一个参数是beforeDeserialize的返回值或者当前属性值(如果没有@beforeDeserialize装饰器)。如果使用了@array装饰器,则会对每一项数组元素都执行这个转换deserialize这个装饰器是最后执行的,第一个参数是beforeDeserialize返回值,@typed返回值,或者当前属性值(如果前面两个装饰器都没设置的话)。在这个装饰器里可以做一些数据订正的操作
这三个装饰器是在执行toClass时才会调用的,同样的,当调用toPlain时也会有对应的装饰器@serialize 、@fterSerialize,结合@typed进行一个相反的过程。下面将这两个转换过程的流程绘制出来。
调用 toClass的过程:
调用 toPlain的过程是调用 toClass的逆过程,但是有些许不一样,有一个注意点就是:在调用 toClass时允许出现一对多的情况,就是一个属性可以派生出多个属性,所以调用调用 toPlain时需要使用 @serializeTarget来标记使用哪一个值作为逆过程的原始值,具体用法可以参考文档。
一个 json 转换工具的更多相关文章
- Json转换工具
import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterx ...
- Json转换工具类(基于google的Gson和阿里的fastjson)
在项目之中我们经常会涉及到字符串和各种对象的转换,为此特地整理了一下常用的转换方法 一.基于com.google.code.gson封装的json转换工具类 1. 在pom.xml文件里面引入gson ...
- java json转换工具类
在java项目中,通常会用到json类型的转换,常常需要对 json字符串和对象进行相互转换. 在制作自定义的json转换类之前,先引入以下依赖 <!--json相关工具--><de ...
- AEM上的一个图片转换工具
目的: 不同情况下,同样一张图片,需要不一样大小/背景/尺寸显示. 例子: dam下面有一张940 x 300 的图片: http://localhost:4502/content/dam/geome ...
- SpringMVC整合FastJson:用"最快的json转换工具"替换SpringMVC的默认json转换
2017年11月23日 09:18:03 阅读数:306 一.环境说明 Windows 10 1709 Spring 4.3.12.RELEASE FastJson 1.2.40 IDEA 2017. ...
- json转换工具类:json<===>list或者对象
public class JsonTools { /** * POJO 转 JSON */ public static String createJsonString(Object object) { ...
- json转换工具类
using System;using System.Collections.Generic;using System.Text;using Newtonsoft.Json;using System.I ...
- json转换工具——fastjson的使用
1.maven依赖<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson< ...
- java util - json转换工具 gson
需要 gson-2.7.jar 包 package cn.java.gson; import com.google.gson.JsonElement; import com.google.gson.J ...
随机推荐
- POJ3255(次最短路)
描述 求1到n的次最短路 开个\(dis[maxn][2]\)的储存距离的二维数组,0储存最短路,1储存次短路 初始化全为正无穷,\(dis[1][0]=0;\) 然后遍历更新时,先尝试更新最短路和次 ...
- (三)Redis &分布式锁
1 Redis使用中的常见问题和解决办法 1.1 缓存穿透 定义:缓存系统都是按照key去缓存查询,如果不存在对应的value,就应该去DB查找.一些恶意的请求会故意查询不存在的key,请求量很大,就 ...
- JS做类型检测到底有几种方法?看完本文就知道了!
JS有很多数据类型,对于不同数据类型的识别和相互转换也是面试中的一个常考点,本文主要讲的就是类型转换和类型检测. 数据类型 JS中的数据类型主要分为两大类:原始类型(值类型)和引用类型.常见的数据类型 ...
- Day_14【IO流】扩展案例2_缓冲字符输出、输入流进行用户名的创建
需求分析 1.项目根目录下建立文件: user.txt,文件中存放用户名和登录密码,格式:用户名,密码,如:aaa,123: 2.user.txt文件中初始存放的用户信息有如下: jack,123 r ...
- [codeforces-543-D div1]树型DP
题意:给一棵树的边标上0或1,求以节点i为源点,其它点到i的唯一路径上的1的边数不超过1条的方案数,输出所有i的答案. 思路:令f[i]表示以节点i为源点,只考虑子树i时的方案数,ans[i]为最后答 ...
- ZOOM火速收购加密公司Kaybase 能否补齐安全短板?
近日,一直因为安全漏洞饱受批评的云视频会议协作工具ZOOM,,其公司全资收购一家初创企业Kaybase,以加强ZOOM系统的隐私保护和安全性. Kaybase公司官网 2020年年初,随着疫情的蔓 ...
- 瞬间教你学会使用java中list的retainAll方法
retainAll方法简介 当我们有两个list集合的时候,我们可以使用retainAll方法求得两个list集合的子集.retainAll是Collection接口中提供的一个方法,各个实现类有自己 ...
- linux 修改时间同步到BIOS
设置时间和日期例如:将系统日期设定成2020年4月14日的命令 命令 : "date -s 04/14/2020" 将系统时间设定成下午5点55分55秒的命令 命令 : " ...
- 萌新学习SpringMVC
前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 这篇SpringMVC被催了很久了,这阵子由于做 ...
- 使用naxsi
naxsi简介 naxsi 是一个nginx 防病毒,防跨站,sql 注入的一个模块.nginx的一个waf ,应用防火墙.非常好配置. naxsi 安装 naxsi 在debina/ubuntu 上 ...