基于antlr3的json分析器实现json到java业务对象转化

分析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>

只有一个依赖包

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(java,json,antlr)