技术标签: Java jsontomap jaon递归解析 json全量解析 java解析json json解析
json报文相信大家都接触过,对于前段JavaScript来说,它是最方便处理的数据格式,而对于后端应用来说,解析json报文并没有xml格式来的那么清晰明了,尤其是对于通用的处理来说很难做到,这里在参考了阿里巴巴的json(非fastjson项目)处理后,十分佩服它的思路,尤其是递归迭代的应用,现在将其源码贴出,供自己以及大家学习。
这里使用了一个包含所有数据类型的模拟json报文,来测试解析类是否一次性全部解析完成了
public static void main(String args[]){
String jsonString = "{\"str\":\"string\",\"num\":100,\"boolean\":true,\"obj\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"list\":[{\"list1\":\"list1\"},{\"list2\":\"list2\"}]}";
JSONReader jr = new JSONReader();
Map map = (Map)jr.read(jsonString);
System.out.println("Json解析完成");
System.out.println("Map----" + map.toString());
System.out.println("list----" + map.get("list").getClass().getName() + ":" + map.get("list"));
System.out.println("str----" + map.get("str").getClass().getName() + ":" + map.get("str"));
System.out.println("num----" + map.get("num").getClass().getName() + ":" + map.get("num"));
System.out.println("boolean----" + map.get("boolean").getClass().getName() + ":" + map.get("boolean"));
System.out.println("obj----" + map.get("obj").getClass().getName() + ":" + map.get("obj"));
}
测试json体中含有了字符串、数字、布尔、对象以及数组类型,调用一次解析得到一个map,里面各种数据结构均相应解析为对应的java类型,下面是测试输出:
Json解析完成
Map----{str=string, boolean=true, obj={key1=value1, key2=value2}, num=100, list=[{list1=list1}, {list2=list2}]}
list----java.util.ArrayList:[{list1=list1}, {list2=list2}]
str----java.lang.String:string
num----java.lang.Long:100
boolean----java.lang.Boolean:true
obj----java.util.HashMap:{key1=value1, key2=value2}
测试说明解析是成功的,所有的数据类型都相应得到了解析,并且所有层的对象和数组也全部解析了,达到了全部迭代解析的效果。
这里先贴出JSONReader类的全部代码:
package roy.json.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA
* Created by Roy.
* Date:2017/5/15
* Time:21:48
*/
public class JSONReader {
private static final Object OBJECT_END = new Object();
private static final Object ARRAY_END = new Object();
private static final Object COLON = new Object();
private static final Object COMMA = new Object();
public static final int FIRST = 0;
public static final int CURRENT = 1;
public static final int NEXT = 2;
private static Map<Character, Character> escapes = new HashMap<Character, Character>();
static {
escapes.put(Character.valueOf('"'), Character.valueOf('"'));
escapes.put(Character.valueOf('\\'), Character.valueOf('\\'));
escapes.put(Character.valueOf('/'), Character.valueOf('/'));
escapes.put(Character.valueOf('b'), Character.valueOf('\b'));
escapes.put(Character.valueOf('f'), Character.valueOf('\f'));
escapes.put(Character.valueOf('n'), Character.valueOf('\n'));
escapes.put(Character.valueOf('r'), Character.valueOf('\r'));
escapes.put(Character.valueOf('t'), Character.valueOf('\t'));
}
private CharacterIterator it;
private char c;
private Object token;
private StringBuffer buf = new StringBuffer();
private char next() {
c = it.next();
return c;
}
private void skipWhiteSpace() {
while (Character.isWhitespace(c)) {
next();
}
}
public Object read(CharacterIterator ci, int start) {
it = ci;
switch (start) {
case FIRST:
c = it.first();
break;
case CURRENT:
c = it.current();
break;
case NEXT:
c = it.next();
break;
}
return read();
}
public Object read(CharacterIterator it) {
return read(it, NEXT);
}
public Object read(String string) {
return read(new StringCharacterIterator(string), FIRST);
}
private Object read() {
skipWhiteSpace();
char ch = c;
next();
switch (ch) {
case '"': token = string(); break;
case '[': token = array(); break;
case ']': token = ARRAY_END; break;
case ',': token = COMMA; break;
case '{': token = object(); break;
case '}': token = OBJECT_END; break;
case ':': token = COLON; break;
case 't':
next(); next(); next(); // assumed r-u-e
token = Boolean.TRUE;
break;
case'f':
next(); next(); next(); next(); // assumed a-l-s-e
token = Boolean.FALSE;
break;
case 'n':
next(); next(); next(); // assumed u-l-l
token = null;
break;
default:
c = it.previous();
if (Character.isDigit(c) || c == '-') {
token = number();
}
}
//logger.debug("token: " + token);
System.out.println("token: " + token); // enable this line to see the token stream
return token;
}
private Object object() {
Map<Object, Object> ret = new HashMap<Object, Object>();
Object key = read();
while (token != OBJECT_END) {
read(); // should be a colon
if (token != OBJECT_END) {
ret.put(key, read());
if (read() == COMMA) {
key = read();
}
}
}
return ret;
}
private Object array() {
List<Object> ret = new ArrayList<Object>();
Object value = read();
while (token != ARRAY_END) {
ret.add(value);
if (read() == COMMA) {
value = read();
}
}
return ret;
}
private Object number() {
int length = 0;
boolean isFloatingPoint = false;
buf.setLength(0);
if (c == '-') {
add();
}
length += addDigits();
if (c == '.') {
add();
length += addDigits();
isFloatingPoint = true;
}
if (c == 'e' || c == 'E') {
add();
if (c == '+' || c == '-') {
add();
}
addDigits();
isFloatingPoint = true;
}
String s = buf.toString();
return isFloatingPoint
? (length < 17) ? (Object)Double.valueOf(s) : new BigDecimal(s)
: (length < 19) ? (Object)Long.valueOf(s) : new BigInteger(s);
}
private int addDigits() {
int ret;
for (ret = 0; Character.isDigit(c); ++ret) {
add();
}
return ret;
}
private Object string() {
buf.setLength(0);
while (c != '"') {
if (c == '\\') {
next();
if (c == 'u') {
add(unicode());
} else {
Object value = escapes.get(Character.valueOf(c));
if (value != null) {
add(((Character) value).charValue());
}
}
} else {
add();
}
}
next();
return buf.toString();
}
private void add(char cc) {
buf.append(cc);
next();
}
private void add() {
add(c);
}
private char unicode() {
int value = 0;
for (int i = 0; i < 4; ++i) {
switch (next()) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = (value << 4) + c - '0';
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
value = (value << 4) + c - 'k';
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
value = (value << 4) + c - 'K';
break;
}
}
return (char) value;
}
}
其核心思路是使用CharacterIterator字符迭代,逐个字符进行解析,然后再read()方法内对字符使用switch进行判断,最先是”{“,所以进入object()方法,object()方法内再执行read()方法获取key值,若是没有迭代到”}”字符,则一直进行递归,解析内层的内容。同时若判断为arraylist,number,boolean都有相应的操作。object()和array()方法内确保了递归的完整性,整体过程空口评述不是很方便,建议有兴趣了解的同学可以将源码执行一遍,然后设置断点,进入debug模式,一步步查看代码的流转。
下面附上执行过程中,解析每个token的输出,也就是JSONReader源码中System.out那行的输出:
token: str
token: java.lang.Object@279f2327
token: string
token: java.lang.Object@2ff4acd0
token: num
token: java.lang.Object@279f2327
token: 100
token: java.lang.Object@2ff4acd0
token: boolean
token: java.lang.Object@279f2327
token: true
token: java.lang.Object@2ff4acd0
token: obj
token: java.lang.Object@279f2327
token: key1
token: java.lang.Object@279f2327
token: value1
token: java.lang.Object@2ff4acd0
token: key2
token: java.lang.Object@279f2327
token: value2
token: java.lang.Object@54bedef2
token: {key1=value1, key2=value2}
token: java.lang.Object@2ff4acd0
token: list
token: java.lang.Object@279f2327
token: list1
token: java.lang.Object@279f2327
token: list1
token: java.lang.Object@54bedef2
token: {list1=list1}
token: java.lang.Object@2ff4acd0
token: list2
token: java.lang.Object@279f2327
token: list2
token: java.lang.Object@54bedef2
token: {list2=list2}
token: java.lang.Object@5caf905d
token: [{list1=list1}, {list2=list2}]
token: java.lang.Object@54bedef2
token: {str=string, boolean=true, obj={key1=value1, key2=value2}, num=100, list=[{list1=list1}, {list2=list2}]}
这里可以看出每次解析的对象,输出中的那些Object是最开始的自定义对象,用来表示对象、数组结束以及冒号,逗号,其实就代表了”}”、”]”、”:”、”,”。
这里体现了java不使用第三方jar包,进行json报文解析的方法,其执行效率没有深入研究,有兴趣的同学可以研究下,并且非常欢迎贴出结果与我讨论。还有,上述方法中没有对非法json结构体进行处理,如果是非法的json格式,就会出现错误的解析结果,而没有异常处理。所以,最好是解析之前,对json结构进行校验,通过后再进行解析,校验打算放在下次再做,谢谢。
文章浏览阅读1.6k次。测试代码:@PostMapping() public void test(@RequestBody Student student){ System.out.println(student.getLover().name()); }class Student{ private Lover lover; public Lover getLover() { return lover; } public void setLover_springboot get请求怎么接收前端传递的枚举数字
文章浏览阅读1.5w次,点赞24次,收藏120次。简单来说就是去量纲后的回归(因为你要比较不同变量之间的显著性的大小,那么带着量纲怎么比,所以先把量纲去掉,然后再比较)官话:为了更为精准的研究影响评价量的重要因素(去除量纲的影响),我们可考虑使用标准化回归系数。_stata两个虚拟变量的交互项
文章浏览阅读203次。有时候安装mysql后使用mysql命令时报错 Can't connect to MySQL server on localhost (10061),或者用net start mysql 时报服务名无效,一般是因为mysql服务没有启动。这时候可以用管理身份运行cmd.exe(注意必须是管理..._c:\program files\mysql\mysql server 5.6\bin>mysqld --install install/remove
文章浏览阅读6.2k次,点赞3次,收藏44次。亚信联创科技校园招聘B 卷考试时间60_分钟 _考试方式(闭)卷(本试卷满分 100 分,答案请写在答题卡上)请不要在问卷上答题或涂改,笔试结束后请务必交回试卷部分内容分值备注一、计算机基础40分C/C++语言基础40分技能部分二、二选一JAVA 语言基础40分三、数据库20分总分100 分第一部分——计算机基础一、选择题(每题 2 分,总分 40分)1.CPU 状态分为目态和管态两种..._亚信科技java实习笔试题
文章浏览阅读1.3k次。3年对一个程序员来说是非常重要的。像我自己本身就是做程序员的,目前的薪资待遇是13K左右,虽然在我所在的公司不是最高的,但在所在的这个城市的消费水平来说,除了日常的开支,包括房租、水电、伙食、人际交往等费用之外,还能留下一部分闲钱自己存起来。不同城市的薪资待遇是不一样的,这主要是由于当地的消费水平和经济发展水平不同,所以如果你想要更高的薪资待遇,就要考虑在一线城市或者经济发达的城市工作。一个有着丰富工作经验的程序员,他的技能水平、经验和能力都比没有经验的程序员更加出色,所以他们的薪资待遇也会更高一些。_三线城市学java
文章浏览阅读975次。1、camera(depth越小,越先渲染)2、sorting layer(值越小,越先渲染)(下面还有个sortingOrder 值越小,越先渲染)3、渲染队列renderQueue(值越小,越先渲染)4、深度值(距离相机越近该值越小,越远该值越大。)..._unity overlaycamera depth
文章浏览阅读1.2k次。一、AAC音频格式种类有哪些AAC音频格式是一种由MPEG-4标准定义的有损音频压缩格式。AAC包含两种格式 ADIF(Audio Data Interchange Format音频数据交换格式)和ADTS(Audio Data transport Stream音频数据传输流)。ADIF特点:可以确定的找到音视频数据的开始,不需要进行在音视频数据流中间开始的解码,它的解码必须在明确的定义开始。应用场景:常用在磁盘文件中。ADTS特点:具有同步字的比特流,解码可以在这个流中任何位置开始。类似于mp_aac adts
文章浏览阅读213次。像要使用Resouce类,必须创建一个 Resouce 文件夹,然后把需要的资源放进去,才可以在代码中设置路径进行访问_unity基本概念
文章浏览阅读2.4k次。指定自定义 CI/CD 配置文件,顾名思义就是在项目中指定文件来代替默认的.gitlab-ci.yml文件的方式来运行流水线。以往我们在使用流水线的时候,都是默认将.gitlab-ci.yml文件存在在项目的跟路径下,但是我们也可以指定备用文件名路径,或者不想在每个项目中来维护这个yml文件,那么通过自定义 CI/CD 配置文件便可以实现。_gitlab配置cicd
文章浏览阅读1w次。出现这个表示如果设置了自动增长,字段类型应该设置为int整型。_sql 错误 [1063] [42000]: incorrect column specifier for column 'id' incorrec
文章浏览阅读1k次。RSA 加载公钥时: Caused by: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.加载公钥代码段:public static String getPubKeyByCer(String cerPath){String pubKey = "";..._java.security.invalidkeyexception: ioexception : derinputstream.getlength():
文章浏览阅读794次。最近鸿蒙HarmonyOS开发相关的消息非常的火,传言华为系手机后续将不再支持原生Android应用,所以对于原Android应用开发对应的Harmony版本也被一系列大厂提上了日程。作为一个名义上的移动端开发工程师((⊙o⊙)…,最近写python多过Android),当人不让要来学习一波。本次的学习计划是实现一个类微信app效果,计划将常规的app效果都实现一下,以便后续如果需要写Harmony应用,可以直接上手。由于我本人有多年的开发经验和多种语言的开发经验,对于Javascript和。_安卓开发鸿蒙写的优美界面