分析json的方式有很多,使用antlr做json的分析在性能上肯定不怎么好,比较涉及到语法语义的分析,同时把json转成java对象的话,那么肯定要基于ast去做,所以在性能上肯定没有原生的好,这里只是提供一种解决思路,比较做原生的json分析,肯定用antlr的话更加的直观一点,下面是antlr的两个分析文件的内容
首先是普通解析器的语法文件
grammar JSON;
options
{
backtrack=true;
memoize=true;
output=AST;
ASTLabelType=CommonTree;
k=1;
}
tokens
{
OBJECT;
ELEMENT;
ARRAY;
STRING;
INTEGER;
DOUBLE;
}
@lexer::header
{
package com.test.json.antlr3;
}
@header
{
package com.test.json.antlr3;
}
@lexer::members{
List errorList = new ArrayList();
public void displayRecognitionError(String[] tokenNames,
RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
errorList.add(hdr+" "+msg);
}
}
@members
{
List errorList = new ArrayList();
public void displayRecognitionError(String[] tokenNames,
RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
errorList.add(hdr+" "+msg);
}
}
// lexer rules
Colon: ':';
Comma: ',';
LBrace: '{';
RBrace: '}';
LBracket: '[';
RBracket: ']';
fragment Dot: '.';
TRUE: 'true';
FALSE: 'false';
NULL: 'null';
DOUBLE_QUOTES :'"';
fragment Digit: '0' .. '9';
fragment HexDigit: ('0' .. '9' | 'A' .. 'F' | 'a' .. 'f');
fragment UnicodeChar: ~('"'| '\\' );
fragment StringChar : ('a'..'z'|'A'..'Z'|'0'..'9'|'_')+;
fragment EscapeSequence
: '\\' ('\"' | '\\' | '/' | 'b' | 'f' | 'n' | 'r' | 't' | 'u' HexDigit HexDigit HexDigit HexDigit)
;
fragment Int: '-'? ('0' | '1'..'9' Digit*);
fragment Frac: Dot Digit+;
fragment Exp: ('e' | 'E') ('+' | '-')? Digit+;
WhiteSpace: (' ' | '\r' | '\t' | '\u000C' | '\n') { $channel=HIDDEN; };
Integer: Int;
Double: Int (Frac Exp? | Exp);
StringLiteral
: '"' ( EscapeSequence | ~('\\'|'"') )* '"'
;
String: DOUBLE_QUOTES StringLiteral* DOUBLE_QUOTES;
IDENTIFIER
:
StringChar*
;
jsonObject
: object
;
jsonArray
: array
;
object
: LBrace (objectElement (Comma objectElement)*)? RBrace
;
objectElement
:
(StringLiteral
|
IDENTIFIER
)
Colon value
;
array
: LBracket value (Comma value)* RBracket
;
value
: StringLiteral
| Integer
| Double
| object
| array
| TRUE
| FALSE
| NULL
;
antlr3的tree grammar的内容
tree grammar JSONWalker;
options
{
backtrack=true;
memoize=true;
output=AST;
ASTLabelType=CommonTree;
tokenVocab=JSON;
k=1;
}
@header {
package com.test.json.antlr3;
}
jsonObject
: object
;
jsonArray
: array
;
object
: LBrace (objectElement (Comma objectElement)*)? RBrace
-> ^(OBJECT objectElement*)
;
objectElement
: (StringLiteral
|
IDENTIFIER) Colon value
-> ^(ELEMENT StringLiteral? IDENTIFIER? value)
;
array
: LBracket value (Comma value)* RBracket
-> ^(ARRAY value+)
;
value
: StringLiteral -> ^(STRING StringLiteral)
| Integer -> ^(INTEGER Integer)
| Double -> ^(DOUBLE Double)
| object
| array
| TRUE
| FALSE
| NULL
;
通过antlr的tool工具生成解析器,为了方便可以写一个ant脚本去实现,具体如下
<java classname="org.antlr.Tool"
fork="true"
failonerror="false"
maxmemory="256m"
>
<classpath>
<pathelement path="${maven}/org/antlr/antlr-complete/3.5.2/antlr-complete-3.5.2.jar" />
</classpath>
<arg value="-o"/>
<arg value="${projectpath}/src/main/java/com/test/json/antlr3"/>
<arg value="-print"/>
<arg value="${projectpath}/grammar/JSON.g"/>
</java>
<java classname="org.antlr.Tool"
fork="true"
failonerror="false"
maxmemory="256m"
>
<classpath>
<pathelement path="${maven}/org/antlr/antlr-complete/3.5.2/antlr-complete-3.5.2.jar" />
</classpath>
<arg value="-o"/>
<arg value="${projectpath}/src/main/java/com/test/json/antlr3"/>
<arg value="-print"/>
<arg value="${projectpath}/grammar/JSONWalker.g"/>
</java>
有了ast语法树以后,剩下的就是要做分析,antlr3有两种方式处理ast语法树,一种是编写treeadapter(需要分析token流,对于我来说难度太大),一种是使用默认的,自己去做遍历,遍历的结果有两种一种是Object一种是Array,但是由于这个只是普通的json原生转化需要实例化具体的业务对象中,我采用先生成语法树,然后生成原生的Java的json对象,通过该对象转化到具体的业务对象中。
转化的过程涉及到了java反射,所以在性能优化上可以做一些缓存处理,在反射处理中使用sun里面的unsafer类,得到最后的转化效果代码如下
public static void main(String[] args) {
User user = JSON.parseObject("{username:null,contact:{mobile:\"1123111311\",qq:\"1231001884\",email:\"[email protected]\"},address:[{email:\"test\",qq:\"419001231\"}]}", User.class);
System.out.println(user.getContact().getMobile()+" "+user.getContact().getEmail()+" "+user.getAddress());
}
把json里面的数据转化成user对象,具体实现可以实例代码
如果要做测试,加入maven的依赖包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr-complete</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
只有一个依赖包