面对Java解析Json字符串的需求,有很多开源工具供我们选择,如google的Gson、阿里巴巴的fastJson。在网上能找到大量的文章讲解这些工具的使用方法。我也是参考这些文章封装了自己的Json解析工具类。这个工具类可以完成Json字符串和对应的实体类对象间的相互转换。用着挺方便的,所以我们之间一直相安无事。直到有一天我遇到了一个新的Json字符串解析需求

将“nodesInService”的值从下面的Json字符串中解析出来。

{

  "beans" : [ {

    "name" :"Hadoop:service=NameNode,name=BlockStats",

    "modelerType" :"org.apache.hadoop.hdfs.server",

    "StorageTypeStats" : [ {

      "key" : "DISK",

      "value" : {

        "blockPoolUsed" : 26618108614,

        "capacityRemaining" : 204199376575,

        "capacityTotal" : 280360910848,

        "capacityUsed" : 26618108614,

        "nodesInService" : 4

      }

    } ]

  } ]

}

按照一贯的做法,需要根据Json字符串的结构定义一个实体类,然后使用Json解析工具类将这个Json字符串映射成对应实体对象,再从对象中将“nodesInService”的值读出来。

这Json字符串看起来结构挺复杂的,数组和对象相互嵌套。此外需求只想要“nodesInService”这一个元素的值,其他元素都不关心。此时我的懒癌无可救药的发作了。感觉为了一个值就要:定义+映射+读取...。有点兴师动众有木有?小题大作有木有?杀鸡用了牛刀有没有?

我只想要一个接口,把原始Json字符串和需要的元素名称传入,接口自动解析出元素的值返回给我。幸好Json字符串的结构看似复杂,其实只有两种元素类型“JSONObject”和“JSONArray”。不管这两种类型数据如何嵌套,只要知道元素类型和名称就能解析出来。所以理论上只要从根元素开始,告诉接口每一级元素的类型和名称,程序就能按图索骥将最后一个元素的值提取出来。

依据以上思路制定了一个元素描述规则:

  1) Json元素描述规则:

    “元素类型:元素名称”,使用“:”分隔。

  2) 类型描述规则:

    A:JSONArray

    E:JSONObject

  3) 元素描述项之间使用“,”间隔。根元素到目标元素描述项从左向右排列。例如上面的“nodesInService”元素的描述项字符串是“A:beans,A:StorageTypeStats,E:value,E:nodesInService”。

使用Gson解析工具实现Json字符串解析函数源码如下:

/**

* json字符串解析函数

*

* @param targetNames

*            目标名字,由“目标类型:目标名称”,以“,”间隔

*            目标类型: A:JSONArray

*                       E:JSONObject,如{"name":"value"}

*            例如: "A:beans,A:StorageTypeStats,E:value,E:nodesInService"

* @param rawJson

*            Json字符串

* @return

*            最后target对应的值

*/

public static String parseJson(String targetNames, String rawJson) {

String retValue = null;

String[] names = targetNames.split(",");

if (names.length <= 0) {

System.out.println("Error: parameter \"targetNames\" is invalid!");

return retValue;

}

try {

JsonParser parser = new JsonParser();  //创建json解析器

JsonObject json = (JsonObject) parser.parse(rawJson);  //创建jsonObject对象

JsonArray jarray = null;

for (int i=0; i < names.length -1; i++) {

System.out.println(names[i]);

String[] subName = names[i].split(":");

if (subName.length != 2) {

System.out.println("Error: parameter \""+ names[i] + "\" is invalid!");

return retValue;

}

String type = subName[0];

String jName = subName[1];

switch (type) {

case "A":

if (null == jarray && null != json) {

jarray = json.getAsJsonArray(jName);

json = null;

}

else if (null != jarray && null == json) {

JsonElement element = jarray.get(0);

JsonObject json1 = element.getAsJsonObject();

jarray = json1.getAsJsonArray(jName);

json = null;

}

else {

System.out.println("Error: parse json string failed!");

return retValue;

}

break;

case "E":

if (null == jarray && null != json) {

JsonElement element = json.get(jName);

json = element.getAsJsonObject();

jarray = null;

}

else if (null != jarray && null == json) {

JsonElement element = jarray.get(0);

JsonObject json1 = element.getAsJsonObject();

json = json1.getAsJsonObject(jName);

jarray = null;

}

else {

System.out.println("Error: parse json string failed!");

return retValue;

}

break;

default:

System.out.println("Error: json type \"" + type + "\" is invalid!");

return retValue;

}

}

String[] subName = names[names.length-1].split(":");

if (subName.length != 2) {

System.out.println("Error: parameter \"targetNames\" is invalid!");

return retValue;

}

String jName = subName[1];

if (null != jarray && null == json) {

JsonElement ele = jarray.get(0);

if (null != ele) {

JsonObject jo = ele.getAsJsonObject();

if (null != jo) {

retValue = jo.get(jName).getAsString();

}

}

return retValue;

}

else if (null == jarray && null != json) {

retValue = json.get(jName).getAsString();

}

else {

System.out.println("Error: parse \"" + jName + "\" failed!");

}

}catch (JsonIOException e) {

e.printStackTrace();

} catch (JsonSyntaxException e) {

e.printStackTrace();

}

return retValue;

}

该函数的功能可以满足这个特定场景的需求,只要将从根元素到目标元素的描述信息字符串和原始Json字符串传入,函数可以直接将目标元素的值解析出来,返回给调用者。使用方式比之实体类映射模式更简洁一些。

细心的小伙伴看了源码可能说“你这个函数不行啊,我要的目标元素是整型或浮点型时,你给我一个String返回值算怎么回事?”,其实不区分元素类型统一返回String类型是为了简化描述规则和函数实现的复杂度。相较在描述规则中增加类型描述,由调用者做一次类型转换更简单一些。

另一位小伙伴又说了“你这个函数有bug, 里面的魔鬼数字将数组类型元素的取值限定在第一个元素上了”。没错,源码确实有这个限制。造成这种现象的原因是我遇到的需求中,所有的数组元素都只有一个值。所以我的懒癌又发作了一次,没有实现更多的预设场景,例如,解析出数组中第N个元素的值、所有元素值或者与另一个元素相关联的元素值。这些场景的需求,使用相似的思路也是可以实现的。

java解析Json字符串之懒人大法的更多相关文章

  1. Java解析json字符串和json数组

    Java解析json字符串和json数组 public static Map<String, String> getUploadTransactions(String json){ Map ...

  2. Java解析Json字符串--复杂对象

    { "name": "三班", "students": [ { "age": 25, "gender" ...

  3. java 解析json字符串

    如果转载我的这篇文章请注明出处,谢谢! 最近工作中,需要解析json格式的字符串,恰好有个例子,感觉不错,拿来分享. 运行这个类需要加载jar包:ezmorph-1.0.6.jar.json-lib- ...

  4. java解析json字符串详解(两种方法)

    一.使用JSONObject来解析JSON数据官方提供的,所以不需要导入第三方jar包:直接上代码,如下 private void parseJSONWithJSONObject(String Jso ...

  5. Java解析Json字符串--数组或列表

    Json示例: [ { "age": 25, "gender": "female", "grades": "三 ...

  6. java解析json字符串

    import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List; ...

  7. java后台处理解析json字符串的两种方式

    简单说一下背景 上次后端通过模拟http请求百度地图接口,得到的是一个json字符串,而我只需要其中的某个key对应的value. 当时我是通过截取字符串取的,后来觉得不太合理,今天整理出了两种处理解 ...

  8. 如何解析json字符串及返回json数据到前端

    前言:最近需要实现的任务是:写若干个接口,并且接口中的请求数据是json格式,然后按照请求参数读取前端提前整理好的json数据,并且将json数据返回到服务器端. 主要的工具:Gson  2.8.2 ...

  9. JackSon解析json字符串

    JackSon解析json字符串 原文:http://blog.csdn.net/java_huashan/article/details/9353903 概述 jackson解析json例子 准备工 ...

随机推荐

  1. MATLAB常用快捷键命令总结

    1. 在命令窗口(Command Window)中: 1)[↑.↓]——切换到之前.之后运行过的命令,可以重复按多次来达到你想要的命令: 2)[Tab]——自动补全.在command窗口,输入一个命令 ...

  2. jsp、freemarker、velocity、thymeleaf页面方案分析

    1.概述在java领域,表现层技术主要有三种, (1)jsp; (2)freemarker; (3)velocity; (4)thymeleaf; 2.jsp优点: 1.功能强大,可以写java代码 ...

  3. 监控虚拟机跟外部的tcp连接

    1.监控虚拟机跟外部的tcp连接,如果连接数超过阈值,就在FORWARD把ip DROP ,并且发送邮件 root@InternetGateway:~# cat /root/scripts/check ...

  4. odoo 11导入外部数据过程记录

    在开发过程中,遇见需要将SQL Server中的数据转移到Pg数据库的情况,那么如何做才能解决这一问题呢? 1.自己写代码,将数据从SQL Server到PG. 2.利用odoo自带的导入功能导入. ...

  5. Ubuntu16.04下完美切换Python版本

    http://blog.csdn.net/beijiu5854/article/details/77897767

  6. Springboot 2.0.4 整合Mybatis出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

    在使用Springboot 2.0.4 整合Mybatis的时候出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are require ...

  7. item 10: 比起unscoped enum更偏爱scoped enum

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 一般情况下,在花括号中声明一个name(包括变量名,函数名),这个 ...

  8. 在 Ionic2 TypeScript 项目中导入第三方 JS 库

    原文发表于我的技术博客 本文分享了在Ionic2 TypeScript 项目中导入第三方 JS 库的方法,供参考. 原文发表于我的技术博客 1. Typings 的方式 因在 TypeScript 中 ...

  9. PHP从入门到精通(一)

    (一)PHP简介和基本知识 PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于 ...

  10. Python_闭包_27

    #闭包:嵌套函数,内部函数 并且必须调用外部函数的变量 def outer(): a = 1 def inner(): print(a) inner() print(inner.__closure__ ...