从JSON中自动生成对应的对象模型

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

从JSON中自动生成对应的对象模型

琴水玉   2020-04-02 我要评论
> 编程的乐趣和挑战之一,就是将体力活自动化,使效率成十倍百倍的增长。 ##需求 做一个项目,需要返回一个很大的 JSON 串,有很多很多很多字段,有好几层嵌套。前端同学给了一个 JSON 串,需要从这个 JSON 串建立对应的对象模型。 比如,给定 JSON 串: ```java {"error":0,"status":"success","date":"2014-05-10","extra":{"rain":3,"sunny":2},"recorder":{"name":"qin","time":"2014-05-10 22:00","mood":"good","address":{"provice":"ZJ","city":"nanjing"}},"results":[{"currentCity":"南京","weather_data":[{"date":"周六今天,实时19","dayPictureUrl":"http://api.map.baidu.com/images/weatherhttps://img.qb5200.com/download-x/dayhttps://img.qb5200.com/download-x/dayu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/nighthttps://img.qb5200.com/download-x/dayu.png","weather":"大雨","wind":"东南风5-6级","temperature":"18"},{"date":"周日","dayPictureUrl":"http://api.map.baidu.com/images/weatherhttps://img.qb5200.com/download-x/day/zhenyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/nighthttps://img.qb5200.com/download-x/duoyun.png","weather":"阵雨转多云","wind":"西北风4-5级","temperature":"21~14"}]}]} ``` 解析出对应的对象模型: ```java public class Domain implements Serializable { private Integer error; private String status; private String date; private List Results; private Extra extra } public class Extra implements Serializable { private Integer rain; private Integer sunny; } public class Recorder implements Serializable { private String name; private String time; private String mood; private Address address } public class Address implements Serializable { private String provice; private String city; } public class Result implements Serializable { private String currentCity; private List Weather_datas; } public class Weather_data implements Serializable { private String date; private String dayPictureUrl; private String nightPictureUrl; private String weather; private String wind; private String temperature; } ``` 怎么办呢 ? 那么复杂的 JSON 串,手写的话,估计得写两个小时吧,又枯燥又容易出错。能否自动生成呢 ?
##算法分析 显然,需要遍历这个 JSON ,分三种情形处理: 1. 值为基本类型: 解析出对应的类型 type 和 字段名 name 2. 值为 JSON 串: 需要递归处理这个 JSON 串 3. 值为 List : 简单起见,取第一个元素,如果是基本类型,按基本类型处理,类型为 List[Type] ;如果是 JSON ,则类型为 List[ClassName],然后再递归处理这个 JSON。
###一个代码实现 第一版程序如下,简单直接。这里用到了一些知识点: * 字符串中的变量引用和方法调用: ```` "${indent()}private ${getType(v)} $k;\n" ```` * 最简单的模板引擎: SimpleTemplateEngine * 函数式编程: 在 parseMap 方法中传入 keyConverter 是为了处理下划线转驼峰。不传则默认不转换。 JsonParser.groovy ```groovy package cc.lovesq.study.json import groovy.json.JsonSlurper import static cc.lovesq.study.json.Common.* class JsonParser { def jsonSlurper = new JsonSlurper() def parse(json) { def obj = jsonSlurper.parseText(json) Map map = (Map) obj parseMap(map, 'Domain', Common.&underscoreToCamelCase) } def parseMap(Map map, String namespace, keyConverter) { def classTpl = classTpl() def fields = "" map.each { k, v -> if (!(v instanceof Map) && !(v instanceof List)) { fields += "${indent()}private ${getType(v)} $k;\n" } else { if (v instanceof Map) { def className = getClsName(k) fields += "${indent()}private $className $k;\n" parseMap(v, convert(className, keyConverter), keyConverter) } if (v instanceof List) { def obj = v.get(0) if (!(obj instanceof Map) && !(obj instanceof List)) { def type = getType(obj) fields += "${indent()}private List<$type> ${type}s;\n" } if (obj instanceof Map) { def cls = getClsName(k) if (cls.endsWith('s')) { cls = cls[0..-2] } fields += "${indent()}private List<${convert(cls,keyConverter)}> ${cls}s;\n" parseMap(obj, convert(cls, keyConverter), keyConverter) } } } } print getString(classTpl, ["Namespace": namespace, "fieldsContent" : fields]) } } ``` Common.groovy ```groovy package cc.lovesq.study.json class Common { def static getType(v) { if (v instanceof String) { return "String" } if (v instanceof Integer) { return "Integer" } if (v instanceof Boolean) { return "Boolean" } if (v instanceof Long) { return "Long" } if (v instanceof BigDecimal) { return "Double" } "String" } def static getClsName(String str) { capitalize(str) } def static capitalize(String str) { str[0].toUpperCase() + (str.length() >= 2 ? str[1..-1] : "") } def static uncapitalize(String str) { str[0].toLowerCase() + (str.length() >= 2 ? str[1..-1] : "") } def static classTpl() { ''' public class $Namespace implements Serializable { $fieldsContent } ''' } def static indent() { ' ' } def static getString(tplText, binding) { def engine = new groovy.text.SimpleTemplateEngine() return engine.createTemplate(tplText).make(binding).toString() } def static convert(key, convertFunc) { convertFunc == null ? key : convertFunc(key) } def static underscoreToCamelCase(String underscore){ String[] ss = underscore.split("_") if(ss.length ==1){ return underscore } return ss[0] + ss.collect { capitalize(it) }.join("") } } ```

##构建与表示分离 第一版的程序虽然简单直接,但总感觉有点粗糙。整个处理混在一起,后续要修改恐怕比较困难。能不能更清晰一些呢 ? 仔细再看下对象模型,可以归结出三个要素: 1. 一个类有一个名字空间 namespace 2. 有一系列 属性名与属性值; 3. 有一系列 子节点类,子节点类可以递归处理。 实际上,对象模型符合树形结构。可以定义一个对象模型的表示: ```groovy class ClassNode implements Node { String className List leafNodes List classNodes Boolean isInList = false } class LeafNode implements Node { String type String name Boolean isList = false } interface Node { String desc() } ``` 在 Node 定义了一个描述自己的方法 desc , LeafNode 和 ClassNode 分别实现自己的 desc 。这样,就完成了对象模型的表示。 接下来,需要完成 ClassNode 的构建。这个过程与第一版的基本类似,只是从直接打印信息变成了添加节点。
###第二个代码实现 第二版的程序如下。有几点值得提一下: 1. 策略模式,分离是三种情况(基本类型、Map, List)的处理。当有多重 if-else 语句,且每个分支都有大段代码达到同一个目标时,就可以考虑策略模式处理了。 2. 构建器。将 ClassNode 的构建单独分离到 ClassNodeBuilder 。 3. 组合模式。树形结构的处理,特别适合组合模式。 ClassNode.groovy ```groovy package cc.lovesq.study.json import org.apache.commons.collections.CollectionUtils import static cc.lovesq.study.json.Common.* class ClassNode implements Node { String className List leafNodes List classNodes Boolean isInList = false ClassNode() { this('') } ClassNode(name) { className = name leafNodes = [] classNodes = [] } @Override String desc() { def clsTpl = Common.classTpl() def fields = "" fields += leafNodes.collect { indent() + it.desc() }.join("\n") def classDef = getString(clsTpl, ["Namespace": className, "fieldsContent" : fields]) if (CollectionUtils.isEmpty(classNodes)) { return classDef } fields += "\n" + classNodes.find { it.isInList == false }.collect { "${indent()}private ${it.className} ${uncapitalize(it.className)}" }.join("\n") def resultstr = getString(clsTpl, ["Namespace": className, "fieldsContent" : fields]) resultstr += classNodes.collect { it.desc() }.join("\n") return resultstr } boolean addNode(LeafNode node) { leafNodes.add(node) true } boolean addNode(ClassNode classNode) { classNodes.add(classNode) true } } ``` LeafNode.groovy ```groovy package cc.lovesq.study.json class LeafNode implements Node { String type String name Boolean isList = false LeafNode(type, name) { this.type = type this.name = name } LeafNode(type, name, isList) { this.type = type this.name = name this.isList = isList } @Override String desc() { isList ? Common.getString("private List<$type> $name;", ["type": type, "name": name]) : Common.getString("private $type $name;", ["type": type, "name": name]) } } ``` ClassNodeBuilder.groovy ```groovy package cc.lovesq.study.json import groovy.json.JsonSlurper import static cc.lovesq.study.json.Common.* class ClassNodeBuilder { def jsonSlurper = new JsonSlurper() def build(json) { def obj = jsonSlurper.parseText(json) Map map = (Map) obj return parseMap(map, 'Domain') } def static parseMap(Map map, String namespace) { ClassNode classNode = new ClassNode() classNode.className = namespace map.each { k, v -> getStratgey(v).add(classNode, k, v) } classNode } def static plainStrategy = new AddLeafNodeStrategy() def static mapStrategy = new AddMapNodeStrategy() def static listStrategy = new AddListNodeStrategy() def static getStratgey(Object v) { if (v instanceof Map) { return mapStrategy } if (v instanceof List) { return listStrategy } return plainStrategy } interface AddNodeStrategy { def add(ClassNode classNode, k, v) } static class AddLeafNodeStrategy implements AddNodeStrategy { @Override def add(ClassNode classNode, Object k, Object v) { classNode.addNode(new LeafNode(getType(v), k)) } } static class AddMapNodeStrategy implements AddNodeStrategy { @Override def add(ClassNode classNode, Object k, Object v) { v = (Map)v def className = getClsName(k) classNode.addNode(parseMap(v, className)) } } static class AddListNodeStrategy implements AddNodeStrategy { @Override def add(ClassNode classNode, Object k, Object v) { v = (List)v def obj = v.get(0) if (!(obj instanceof Map) && !(obj instanceof List)) { def type = getType(obj) classNode.addNode(new LeafNode("$type", "${type}s", true)) } if (obj instanceof Map) { def cls = getClsName(k) if (cls.endsWith('s')) { cls = cls[0..-2] } classNode.addNode(new LeafNode("${cls}", "${cls}s", true)) def subClassNode = parseMap(obj, cls) subClassNode.isInList = true classNode.addNode(subClassNode) } } } } ``` JsonParserV2.groovy ```groovy package cc.lovesq.study.json import groovy.json.JsonOutput class JsonParserV2 { def parse(json) { def classNode = new ClassNodeBuilder().build(json) println(JsonOutput.toJson(classNode)) print classNode.desc() } } ```
##小结 通过编写程序,从 JSON 串中自动生成对应的对象模型,使得这个过程自动化,让类似事情的效率成倍的增长了。原来可能要花费几十分钟甚至一个小时之多,现在不到三秒。 让效率成倍增长的有效之法就是提升代码和方案的复用性,自动化手工处理。在日常工作中,是否可以想到办法,让手头事情的处理效率能够十倍百倍的增长呢 ? 这个想法看似有点疯狂,实际上,更多的原因是人们没有这么思考过吧。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们