娱乐头条[:从JSON<中自动生成对应的对>象模型

admin 2个月前 (04-06) 科技 5 1

「进入」sunbet『官网手机版登』陆

欢迎「进入」sunbet『官网手机版登』陆!Sunbet “申博提供申博〖开户〗”(sunbet〖开户〗)、SunbetAPP「下载」、Sunbet客户端「下载」、Sunbet(代)理合作等业务。

,

‘编程的乐趣【《和》】挑’战之一,“就是{将}”体力活自动化,【使效率成十倍百倍的】增「长」。

『需求』

(做一个项)目,「需要返回一个很」大的 JSON (“〖 『《串》』[〗”),『有很多很多很多字段』,「有好几层嵌套」。<前端同学给了一个> JSON (“〖 『《串》』[〗”),《需要<从>这个》 JSON (“〖 『《串》』[〗”)建立对应的对(象模型)。
‘比如’,《给定》 JSON (“〖 『《串》』[〗”):

{"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/weather/day/dayu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/dayu.png","weather":"‘大’雨","wind":"{东}南风5-6「『级』」","temperature":"18"},{"date":"‘周’日","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/zhenyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"阵雨转多云","wind":"(西北风)4-5「『级』」","temperature":"21~14"}]}]}

{解析出}对应的对(象模型):

public class Domain implements Serializable {
    private Integer error;
    private String status;
    private String date;
    private List<Result> 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_data> 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 “是为了处理下”划线转驼【峰】。(不传)则默认不转换。
  • JSON <转换为>对象: jsonSlurper.parseText(json)

JsonParser.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

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. “<有一系列>属性”,〖每个属性〗有属性名与属{性值},【可】称为 LeafNode;
  3. <有一系列> ‘子节点类’ ClassNode,‘子节点类’可以递归处理。

〖「实」际上〗,〖对象〗模型符合树形结构。 可[以定义一个对(象模型)的{表「示」}:

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<LeafNode> leafNodes = []
    List<ClassNode> classNodes = []
    Boolean isInList = false

    @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
    }
}

class LeafNode implements Node {

    String type
    String name
    Boolean isList = false

    @Override
    String desc() {
        isList ? Common.getString("private List<$type> $name;", ["type": type, "name": name]) :
                Common.getString("private $type $name;", ["type": type, "name": name])
    }

}

interface Node {
   String desc()
}

‘“在”’ Node {定义了一个描述自己}的「方法」 desc , LeafNode 【《和》】 ClassNode 〖分别实现自己〗的 desc 。『这样』,就完成了对(象模型)的{表「示」}。

「接下」来,<需要完>成 ClassNode 『的』{<构建>}。<这个过程与第一>版的基<本类似>,「只」是<从>直接打印信息变《成了添加节点》。


{<构建>}

{<构建>} ClassNode 《的》实现如下。【有几点值得提一】下:

  1. 策略模式。“分离了三种情”况(基本类型、Map, List)的处理。‘当有多重’ if-else 「语句」,(且)每个分支都有大段代(码达)到同一个目标时,『就可以考虑策』略模式处「理了」。
  2. {<构建>}器。{将} ClassNode 『的』{<构建>}单独分离到 ClassNodeBuilder 。
  3. 《组合模式》。【树形结构】的处理,特别适合《组合模式》。
  4. (命名构造)。使用(命名构造)器,『<从>而免写了』一些构造器。

ClassNodeBuilder.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(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(type: getType(v), name: 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", name: "${type}s", isList: true))
            }
            if (obj instanceof Map) {
                def cls = getClsName(k)
                if (cls.endsWith('s')) {
                    cls = cls[0..-2]
                }
                classNode.addNode(new LeafNode(type: "${cls}", name: "${cls}s", isList:  true))

                def subClassNode = parseMap(obj, cls)
                subClassNode.isInList = true
                classNode.addNode(subClassNode)
            }
        }
    }

}


「小结」

〖通过编写〗程序,<从> JSON (“〖 『《串》』[〗”)中自动生成对应的对(象模型),「使得」这个过程自动化,〖让类似事情〗的效率成倍的增「长」了。「原来可能要花」费几十分《钟甚至》一个小时之多,现‘“在”’不到三秒。

让效率成倍增「长」的有效之法就是提升代码【《和》】方<案的复用>性,『自动化手工处理』。‘“在”’日常工作中,「是否可以想」到办法,<让手>头事情的处理效率能够十倍百倍的增「长」呢 ? ‘这个想法看似有点疯’狂,〖「实」际上〗,‘更多的原因是人们没’有<这么思考过>吧。

新2备用网址声明:该文看法仅代表作者自己,与本平台无关。转载请注明: 娱乐头条[:从JSON<中自动生成对应的对>象模型

网友评论

  • (*)

最新评论

  • 伸博新网 2020-04-06 00:01:06 回复

    sunbet 申博Sunbet 申博www.1888ss.com秉承以客为先的理念,多年运营、专业团队、专注服务、值得信赖。就是很好,谁损也没用

    1

文章归档

站点信息

  • 文章总数:445
  • 页面总数:0
  • 分类总数:8
  • 标签总数:815
  • 评论总数:110
  • 浏览总数:3801