预期效果
输入t.json:
输出t.xml:
原有语法文件
// Derived from http://json.org
grammar JSON;
json: object
| array
;
object
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair: STRING ':' value ;
array
: '[' value (',' value)* ']'
| '[' ']'
;
value
: STRING #String
| NUMBER #Atom
| object #ObjectValue
| array #ArrayValue
| 'true' #Atom
| 'false' #Atom
| 'null' #Atom
;
STRING : '"' (ESC | ~["\])* '"' ;
fragment ESC : '\' (["\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
NUMBER
: '-'? INT '.' [0-9]+ EXP? // 1.35, 1.35E-9, 0.3, -4.5
| '-'? INT EXP // 1e10 -3e4
| '-'? INT // -3, 45
;
fragment INT : '0' | [1-9] [0-9]* ; // no leading zeros
fragment EXP : [Ee] [+-]? INT ; // - since - means "range" inside [...]
WS : [ \t\n\r]+ -> skip ;
123456789101112131415161718192021222324252627282930313233343536373839404142434445
我的思考
- 首先要区分JSON两种形式:object(键值对)和array。它们在xml中表现形式也是不同的。object是单纯的
< tag > value < /tag >。而array需要将每个元素前后插入
< element >和< /element >。 - 如何实现嵌套,且object的嵌套与array本身相同,都需要有
\t。 - 何时去打印呢,是在
enter和exit时候吗。
现有语法文件
注意我们在object和array的不同子分支上,打上了不同的标签。
// Derived from http://json.org
grammar JSON;
json: object
| array
;
object
: '{' pair (',' pair)* '}' #AnObject
| '{' '}' #EmptyObject
;
pair: STRING ':' value ;
array
: '[' value (',' value)* ']' #ArrayOfValues
| '[' ']' #EmptyOfValues
;
value
: STRING #String
| NUMBER #Atom
| object #ObjectValue
| array #ArrayValue
| 'true' #Atom
| 'false' #Atom
| 'null' #Atom
;
STRING : '"' (ESC | ~["\])* '"' ;
fragment ESC : '\' (["\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
NUMBER
: '-'? INT '.' [0-9]+ EXP? // 1.35, 1.35E-9, 0.3, -4.5
| '-'? INT EXP // 1e10 -3e4
| '-'? INT // -3, 45
;
fragment INT : '0' | [1-9] [0-9]* ; // no leading zeros
fragment EXP : [Ee] [+-]? INT ; // - since - means "range" inside [...]
WS : [ \t\n\r]+ -> skip ;
具体流程
针对上面的思考,我们拟出了一个方案。
-
首先我们不采用进入打印,退出打印这种一步一步的操作,而是通过
ParseTreeProperty的辅助数据结构,将每个结点的值保存起来,在最后的exitJson里保存整个结构。这时一个由下而上的保存过程。观察下图,我们可以从最下层的value开始保存,value->pair->object->json。
因此需要先创建一个辅助数据结构,并且定义get和set接口:
ParseTreeProperty<String> xml=new ParseTreeProperty<String>(); public void setXML(ParseTree ctx,String s){ xml.put(ctx,s); } public String getXML(ParseTree ctx){ return xml.get(ctx); } 12345 -
由下至上,我们先处理value的几个分支。
NUMBER ‘true’ ‘false’ ‘null’它们可以创建同一个标签,处理函数就是exit时保存到xml即可。public void exitAtom(JSONParser.AtomContext ctx) { setXML(ctx,ctx.getText()); } -
STRING的道理类似,但由于本身带有””,在被getText获取前需要手动去掉双引号,再保存到结点里。public static String stripQuotes(String s) { if ( s==null || s.charAt(0)!='"' ) return s; return s.substring(1, s.length() - 1); } public void exitString(JSONParser.StringContext ctx) { setXML(ctx,stripQuotes(ctx.getText())); } 12345678 -
object在这里我们虽然创建了新的标签,但是由于是嵌套的,它最终会调用
AnObject或者EmptyObject标签存到xml中。所以我们直接setXML(ctx,getXML(ctx.object())即可。public void exitObjectValue(JSONParser.ObjectValueContext ctx) { setXML(ctx,getXML(ctx.object())); } -
array同理,我们直接setXML(ctx,getXML(ctx.array())即可。
public void exitArrayValue(JSONParser.ArrayValueContext ctx) { setXML(ctx,getXML(ctx.array())); } -
value保存完以后,我们考虑pair该如何保存。很简单,其实就是
< STRING>value< /STRING>的格式,只不过要将标签的双引号给去掉,并且在最后加上\n。public void exitPair(JSONParser.PairContext ctx) { String tag=stripQuotes(ctx.STRING().getText()); String x=String.format("<%s>%s</%s>\n", tag,getXML(ctx.value()),tag); setXML(ctx,x); } -
Pair保存完我们就回到了开始的json的两个分支,首先是object。很简单,创建一个
String构造器,将pair的值一个个赋给它。public void exitAnObject(JSONParser.AnObjectContext ctx) { StringBuilder buf=new StringBuilder(); buf.append('\n'); for(JSONParser.PairContext v:ctx.pair()){ buf.append(getXML(v)); } setXML(ctx,buf.toString()); } -
数组与object略微不同的地方在于,每个元素前需要加
< element>,
后需要加< /element >,插入value的时候注意点就好。public void exitArrayOfValues(JSONParser.ArrayOfValuesContext ctx) { StringBuilder buf=new StringBuilder(); buf.append('\n'); for(JSONParser.ValueContext v:ctx.value()){ buf.append("<element>"); buf.append(getXML(v)); buf.append("</element>"); buf.append('\n'); } setXML(ctx,buf.toString()); } -
后来到
json部分,我们将json的第一个孩子结点的内容传给它就算完事了。public void exitJson(JSONParser.JsonContext ctx) { setXML(ctx,getXML(ctx.getChild(0))); }
运行结果
如何将嵌套造成的缩进效果显式出来是一个问题,我们后续会对其解决。