笔者在调用Google Calendar APIs的GetColors过程当中(具体关于Google Calendar API已经Google API的介绍请见我其他的博文,当前我们只是拿Google Calendar API返回的结果举一个例子),JSON返回的数据中,出现了以数字作为键(key)的数据;但是因为我们在企业应用集成中,有时候需要把JSON数据转换成XML数据;那么这个时候,JSON数据中的键(key)映射到XML数据中将成为XML数据的节点名字(Node Name),如果JSON中的键(key)是数字的话,映射到XML数据的时候就会出错,因为XML规范中不支持把纯粹的数字作为XML数据的节点名字;这种情况下,我们就需要在对JSON数据转换成XML数据之间,进行一下处理。
以Google 日历(Calendar) API中的获取颜色(Get Color)的数据为例子,请见下面的数据。Calendar键(key)中的值,是一个包含多个对象的对象;每个对象的键(key)是一个数字,而且数字呈现递增的趋势;同理Event键(key)中的值也有同样的特征;如果把这样的数据转换成XML的数据的话,转换将不会成功。
{ "kind": "calendar#colors", "updated": "2012-02-14T00:00:00.000Z", "calendar": { "1": { "background": "#ac725e", "foreground": "#1d1d1d" }, "2": { "background": "#d06b64", "foreground": "#1d1d1d" }, "3": { "background": "#f83a22", "foreground": "#1d1d1d" } }, "event": { "1": { "background": "#a4bdfc", "foreground": "#1d1d1d" }, "2": { "background": "#7ae7bf", "foreground": "#1d1d1d" } } }
比如,我们把上面的数据,在一个在线的JSON转XML的网站上进行转换,http://www.freeformatter.com/json-to-xml-converter.html,在这个在线工具里面将会提示下面的错误信息:
那么解决的办法是什么呢?解决的办法就是把上面的带有数字的键(key)值对集合,变成一个没有数字的键(key)的数组,如下面的格式,
{ "kind": "calendar#colors", "updated": "2012-02-14T00:00:00.000Z", "calendar": [{ "background": "#ac725e", "foreground": "#1d1d1d" }, { "background": "#d06b64", "foreground": "#1d1d1d" }, { "background": "#f83a22", "foreground": "#1d1d1d" } ] }, "event": [{ "background": "#a4bdfc", "foreground": "#1d1d1d" }, { "background": "#7ae7bf", "foreground": "#1d1d1d" } ] } }
转换后的XML的数据如下,
<?xml version="1.0" encoding="UTF-8"?> <root> <calendar> <element> <background>#ac725e</background> <foreground>#1d1d1d</foreground> </element> <element> <background>#d06b64</background> <foreground>#1d1d1d</foreground> </element> <element> <background>#f83a22</background> <foreground>#1d1d1d</foreground> </element> </calendar> <kind>calendar#colors</kind> <updated>2012-02-14T00:00:00.000Z</updated> </root>
那么问题来,如果用代码自动来实现转换且不引入任何的除JDK自带的API之外的其他的jar包呢?具体算法,请见下面的代码。
1. NumberKeyPosition Java Bean: 用来存储数字键(key)在JSON字符串中出现的开始位置,结束位置,以及是否是第一个数字键(key),是否是最后一个数字数字键(key),比如上面的中下面的数据,是calendar的第一个,所以isFirstOne的值为True。
"1": { "background": "#ac725e", "foreground": "#1d1d1d" }
public class NumberKeyPosition { private int startPos; private int endPos; private boolean isFirstOne=false; private boolean isLastOne=false; public int getStartPos() { return startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public boolean isFirstOne() { return isFirstOne; } public void setFirstOne(boolean isFirstOne) { this.isFirstOne = isFirstOne; } public boolean isLastOne() { return isLastOne; } public void setLastOne(boolean isLastOne) { this.isLastOne = isLastOne; } }
2. CovertNumberKeyAsArrayUtil 类:这个类就是执行上面处理JSON数据的具体的执行算法的类了。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CovertNumberKeyAsArrayUtil { private String jsonString; public void setJsonString(String jsonString) { this.jsonString = jsonString; } public CovertNumberKeyAsArrayUtil(){ } public CovertNumberKeyAsArrayUtil(String jsonString){ this.jsonString=jsonString; } public String readFileAsString(String fileName){ InputStream ins=this.getClass().getResourceAsStream(fileName); StringBuffer sBuffer=new StringBuffer(); BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(ins)); String lineString=""; try { while((lineString=bufferedReader.readLine())!=null){ sBuffer.append(lineString); } } catch (IOException e) { e.printStackTrace(); } //System.out.println(sBuffer.toString()); return sBuffer.toString(); } /** * getNumberKeyPositions * @param jsonString * @return */ private List<NumberKeyPosition> getNumberKeyPositions(){ List<NumberKeyPosition> lsNumberKeyPosition=new ArrayList<NumberKeyPosition>(); String expression="\"\\d*\":"; Pattern p = Pattern.compile(expression); Matcher m = p.matcher(jsonString); StringBuffer sb = new StringBuffer(); int prevIndex=0; while (m.find()) { NumberKeyPosition numberKeyPosition=new NumberKeyPosition(); String matchString=m.group(); int currentIndex=Integer.parseInt(matchString.trim().replace("\"", "").replace(":", "")); numberKeyPosition.setStartPos(m.start()); numberKeyPosition.setEndPos(m.end()); //System.out.println("Start Pos:"+m.start()); //System.out.println("End Pos:"+m.end()); if(currentIndex==1){ numberKeyPosition.setFirstOne(true); if(prevIndex==1){ if(lsNumberKeyPosition.size()>0){ lsNumberKeyPosition.get(lsNumberKeyPosition.size()-1).setLastOne(true); } }else{ if(prevIndex>1){ lsNumberKeyPosition.get(lsNumberKeyPosition.size()-1).setLastOne(true); } } }else{ numberKeyPosition.setFirstOne(false); numberKeyPosition.setLastOne(false); } prevIndex=currentIndex; lsNumberKeyPosition.add(numberKeyPosition); } if(lsNumberKeyPosition!=null&&lsNumberKeyPosition.size()>0){ lsNumberKeyPosition.get(lsNumberKeyPosition.size()-1).setLastOne(true); } return lsNumberKeyPosition; } /** * * @param endOfLastPositionString * @return * @Test data: { "background": "#f83a22", "foreground": "#1d1d1d" } }, "event": { * { "background": "#f83a22", "foreground": "#1d1d1d" } }, "event": { */ public String addBracket4EndPostion(String endOfLastPositionString){ StringBuffer sBuffer=new StringBuffer(); int leftBracketCount=0; int leftBracketCountFirstIndex=endOfLastPositionString.indexOf('{'); boolean isAddBracketSucc=false; boolean isRemovedLaterBrace=false; for(int i=0;i<endOfLastPositionString.length();i++){ if(endOfLastPositionString.charAt(i)=='{'){ leftBracketCount++; } else if(endOfLastPositionString.charAt(i)=='}'){ leftBracketCount--; } sBuffer.append(endOfLastPositionString.charAt(i)); if(leftBracketCount==0&&i>leftBracketCountFirstIndex&&!isAddBracketSucc){ sBuffer.append(']'); isAddBracketSucc=true; continue; } if(isAddBracketSucc&&!isRemovedLaterBrace&&endOfLastPositionString.charAt(i)=='}'){ int lenStringBuffer=sBuffer.length(); sBuffer=new StringBuffer(sBuffer.substring(0, lenStringBuffer-1)); isRemovedLaterBrace=true; } } return sBuffer.toString(); } private String trimRightBrace(String tmpString) { String trimRightBraceString = ""; if (tmpString != null) { tmpString=tmpString.trim(); int len = tmpString.length(); if (len > 1 && tmpString.endsWith("{")) { trimRightBraceString = tmpString.substring(0, len - 2); } } return trimRightBraceString; } /** * getRemovedNumberKeyJSONString * @return */ public String getRemovedNumberKeyJSONString(){ //String removedNumberKeyJSONString=null; List<NumberKeyPosition> lsNumberKeyPosition=this.getNumberKeyPositions(); StringBuffer sbBuffer=new StringBuffer(); if(lsNumberKeyPosition!=null&&lsNumberKeyPosition.size()>0){ for(int i=0;i<lsNumberKeyPosition.size();i++){ NumberKeyPosition currentnumberKeyPosition=lsNumberKeyPosition.get(i); if(i==0){ String tmpString=jsonString.substring(0, currentnumberKeyPosition.getStartPos()).trim(); sbBuffer.append(trimRightBrace(tmpString)); sbBuffer.append("["); }else{ NumberKeyPosition preNumberKeyPosition=lsNumberKeyPosition.get(i-1); if(currentnumberKeyPosition.isFirstOne()){ sbBuffer.append("["); }else{ if(currentnumberKeyPosition.isLastOne()){ sbBuffer.append(jsonString.substring(preNumberKeyPosition.getEndPos(), currentnumberKeyPosition.getStartPos())); if(i<lsNumberKeyPosition.size()-1){ NumberKeyPosition nextNumberKeyPosition=lsNumberKeyPosition.get(i+1); String endOfLastPositionString=jsonString.substring(currentnumberKeyPosition.getEndPos(),nextNumberKeyPosition.getStartPos()).trim(); sbBuffer.append(addBracket4EndPostion(trimRightBrace(endOfLastPositionString))); }else{ String endOfLastPositionString=jsonString.substring(currentnumberKeyPosition.getEndPos(),jsonString.length()); sbBuffer.append(addBracket4EndPostion(endOfLastPositionString)); } }else{ sbBuffer.append(jsonString.substring(preNumberKeyPosition.getEndPos(), currentnumberKeyPosition.getStartPos())); } } } } } return sbBuffer.toString(); } public static void main(String[] args) { CovertNumberKeyAsArrayUtil covertNumberKeyAsArrayUtil=new CovertNumberKeyAsArrayUtil(); covertNumberKeyAsArrayUtil.setJsonString(covertNumberKeyAsArrayUtil.readFileAsString("jsonColorNumber.json")); String removedNumberKeyJSONString=covertNumberKeyAsArrayUtil.getRemovedNumberKeyJSONString(); System.out.println(removedNumberKeyJSONString); } }
如何用Google APIs和Google的应用系统进行集成(7)----在把JSON转换成XML数据过程中,JSON数据中包含违背XML数据规范:XML节点名不能只是数字的Java解决方案