API 관련 테스팅 프레임워크를 만들면서 json을 출력하게 하는데..
그냥 출력하려니.. 양심에 찔려서 인터넷을 뒤져 봤더니 gson을 이용하면 깔끔하게 출력해 주는 Tip이 있다.

http://stackoverflow.com/questions/8596161/json-string-tidy-formatter-for-java

JsonParser parser = new JsonParser();
Gson gson = new GsonBuilder().setPrettyPrinting().create();

JsonElement el = parser.parse(jsonString);
jsonString = gson.toJson(el);
위와 같이 간단히 해결하고 한동한 있고 있었는데.
오늘 독일팀이 잘 쓰고 있는데 좀 문제가 있네 하면서 보내준 내용을 보니
json 리턴 값에 HTML 태그랑, JavaScript가 있어서 출력값이 깨져서 나오게 된다.

흐미... 내가 왜 HTML이 포함될 수 있다는 걸 까먹고 있었는감...-.-
어쨋든 뭐...JSON 결과를 HTML escaping하면 간단히 해결되지 않을까 싶었는데..
테스트 해보니. JSON 내의 " 까지도 escaping이 되버리는 바람에 formatting 한게 물거품이 되어 버리고 만다.-.-
( formattting 한후 <pre></pre> 태그를 앞 뒤로 붙여 formatting한 결과를 보여 주어서...)

이런 저런 생각을 계속 하다 보니 결국 방법은 JSON 데이터를 Parsing 한 후 Field 를 Traversing 하며 출력해 주면서 Field 값이 문자열일 경우에 HTML escaping을 하면 될 것 같다.

마침 다른 이슈로 recursive 하게 json traversing하는 로직을 만들어 놓은 게 있어서 사용을 했더니 결국 HTML escaping이 깔끔하게 해결되었다.

public String getPrettyHtmlJson(String content){
try {
Object jsonContent = JSONParser.parseJSON(content);
return "<pre>"+getPrettyHtmlJsonElement(jsonContent, 0,false)+"</pre>";

} catch (JSONException e) {
e.printStackTrace();
return content;
}
}

private String getPrettyHtmlJsonElement(Object object, int indent, boolean indentNeed) throws JSONException{
String content = "";
if ( object instanceof JSONArray){
JSONArray jsonArray = (JSONArray) object;
if ( indentNeed)
for(int i = 0; i < indent; i++) content+=" ";
content+= "[ \n";
for(int j = 0; j < jsonArray.length(); j++){
if ( j > 0 ) {
content+=", \n";
//for(int i = 0; i < indent; i++) content+=" ";
}
content += getPrettyHtmlJsonElement(jsonArray.get(j), indent+1, true);
}
content += "\n";
for(int i = 0; i < indent; i++) content+=" ";
content+= "]";
return content;
}
else if ( object instanceof JSONObject){
if ( indentNeed)
for(int i = 0; i < indent; i++) content+=" ";
content+= "{\n";
JSONObject jsonObject = (JSONObject) object;
String[] names = JSONObject.getNames(jsonObject);
for(int j =0 ; j < names.length; j++){
String name = names[j];
for(int i = 0; i <= indent; i++) content+=" ";
content+= "\""+name+"\" :";
Object fieldObject = jsonObject.get(name);
if ( fieldObject instanceof JSONArray)
content += getPrettyHtmlJsonElement(fieldObject, indent+1, false);
else if ( fieldObject instanceof JSONObject)
content += getPrettyHtmlJsonElement(fieldObject, indent+1, false);
else if ( fieldObject instanceof String){
content += "\""+StringEscapeUtils.escapeHtml4(fieldObject.toString())+"\"";
}
else
content += "\""+fieldObject.toString()+"\"";
if ( j + 1 < names.length )
content += ",";
content +=" \n";
}
for(int i = 0; i < indent; i++) content+=" ";
content+= "}";
return content;
}
else{
for(int i = 0; i < indent; i++) content+=" ";
content+= "\""+object.toString()+"\"";
return content;
}
}

잘 되었네.. 하고 접을 까 싶었더니...
Field 순서가 뒤죽 박죽이다.
구글링을 해보니.. JSON은 순서를 보장하지 않는 필드 List라는 문구가 나온다.
흐... 그래서 field ignore 시킬 때 JSON Parsing을 하면 순서가 뒤바뀌어서 나왔었구나...

뭐.. 순서가 바뀌어 출력되도 스펙 내용에 따르면 별 문제는 없는데..
그래도 개발의 양심이라는 것이...
결국은 좀더 조사해보니...
JSONObject map을 HashMap에서 LinkedHashMap으로 바꾸면 해결 된다고 한다.

http://stackoverflow.com/questions/4515676/keep-the-order-of-the-json-keys-during-json-conversion-to-csv

마침...
J
son Assert가 org.json.* 구조를 이용하는데.. 이 library는 마침 JSON.org에서 다운 받은 jar를 사용하고 있어서..
source를 받아서 위에 얘기한 대로 LinkedHashMap으로 바꾸고 나니 문제점 깔끔하게 해결...

덕분에 field ignore 할 때도 필드 순서 보장하게 되고...
HTML escapaing 문제도 해결하고 ... formatting때문에 사용한 gson library도 빼고...

골치아픈 HTML escaping 이슈가 오히려 전화위복이 되어 더 깔끔한 프레임웤이 완성되었다.


Posted by headiron
,