Clean Code that Works.

XML 을 JSON으로..2

Java2010. 4. 5. 23:01
이전의 포스트에 이은 2탄.

이전에 XML을 JSON으로 만들었는데...

이것을 Spring MVC에서 @ResposeBody 형태로 해서 리턴을 하게 되면...


org.codehaus.jackson.map.JsonMappingException: null object
위 오류를 찍어 준다.(항상 발생하는것은 아니고)
왜 이럴까. 하고 봤더니..
값이 null인 것이 있었다.. 위의 XML에는 값이 null 인게 없었는데, 값이 널일 경우에는 JSON에 value 쪽에 null이 들어 가게 된다.

하여, 값이 null일 경우에는 다른 값("") 아무 값도 없는 이런 값이 필요하게 되어 net.sf.json 패키지의
XMLSerializer를 살펴 보게 되었다.

로직을 살펴 보면 값이 없는 경우에 null을 표시 하도록 되어 있는 것을 확인 할 수 있었다.

하여 저 부분을 null 이 아닌 ""을 리턴하도록 변경 하여 처리 하였다.

네이버 지도 OPEN API 사용하는데...
위치 검색 해서 좌표 구하는 부분에서.. 네이버에서는 XML을 리턴한다.

그러므로.. 크로스 도메인 문제로 jQuery로는 사용이 불가 하다.(JSON으로 리턴해주면 되는데!!!!)
하여 서버사이드에서 처리를 해 줘야 한다.

서버에서 URL 로 콜 해서 데이터들을 가져온 다음..
(이걸 XML로 변환한 다음에 JSON으로 변환해야 될줄 알았는데 그냥 데이터(XML 형식의 TEXT)를 JSON으로 변환 시켜도 된다)

그럼 이런 결과가 찍힌다.
<?xml version="1.0" encoding="euc-kr" ?><geocode xmlns="naver:openapi"><userquery><![CDATA[경기도성남시정자1동25-1]]></userquery><total>1</total><item><point><x>321063</x><y>529727</y></point><address>경기도 성남시 분당구 정자1동 25-1</address><addrdetail><sido><![CDATA[경기도]]><sigugun><![CDATA[성남시 분당구]]><dongmyun><![CDATA[정자1동]]><rest><![CDATA[25-1]]></rest></dongmyun></sigugun></sido></addrdetail></item></geocode>

이것을 JSON으로 변경 해야 하는데
이미 공개된 API들이 있으므로 그것을 사용하자. ㅋ

org.json(maven) 과 net.sf.json

org.json의 경우 XML이라는 클래스에 toJSONObject() 메서드를 제공 한다. 하여 결과를 찍어 보면,
깔끔한 JSON 코드가 나온다.
{"geocode":{"total":1,"userquery":"경기도성남시정자1동25-1","item":{"point":{"y":529727,"x":321063},"address":"경기도 성남시 분당구 정자1동 25-1","addrdetail":{"sido":{"sigugun":{"content":"성남시 분당구","dongmyun":{"content":"정자1동","rest":"25-1"}},"content":"경기도"}}},"xmlns":"naver:openapi"}}


net.sf.json 에서는 XOM이라는 jar 파일이 필요하다.
XMLSerializer에서 의존하고 있는것이 있다.
하여 실행을 해 보면 아래와 같이 표시된다.
{"geocode":{"total":1,"userquery":"경기도성남시정자1동25-1","item":{"point":{"y":529727,"x":321063},"address":"경기도 성남시 분당구 정자1동 25-1","addrdetail":{"sido":{"sigugun":{"content":"성남시 분당구","dongmyun":{"content":"정자1동","rest":"25-1"}},"content":"경기도"}}},"xmlns":"naver:openapi"}}
{"@xmlns":"naver:openapi","userquery":"경기도성남시정자1동25-1","total":"1","item":{"point":{"x":"321063","y":"529727"},"address":"경기도 성남시 분당구 정자1동 25-1","addrdetail":{"sido":{"#text":"경기도","sigugun":{"#text":"성남시 분당구","dongmyun":{"#text":"정자1동","rest":"25-1"}}}}}}

약간 틀리다.
org.json에서는 text 값들을 content로
net.sf.json은 #text로 표시해준다.

왼지 org.json이 깨끗해 보이긴 하나.. 이미 net.sf.json을 사용중이므로..
아래것으로 해야겠다.

테스트 코드는 아래 것.

@Test
    public void getXmlData() {
        URL naverUrl;
        StringBuffer sb = new StringBuffer();
        try {
            naverUrl = new URL(
                    "http://map.naver.com/api/geocode.php?key=키입력&query=경기도성남시정자1동25-1");
           
            BufferedReader in = new BufferedReader(new InputStreamReader( naverUrl.openStream()));
            String inputLine;

            while ((inputLine = in.readLine()) != null)
                sb.append( inputLine.trim());
           
            in.close();
           
            System.out.println( sb.toString());
           
            org.json.JSONObject jsonObj = XML.toJSONObject( sb.toString());
            System.out.println(jsonObj);
            JSONObject obj = (JSONObject) new XMLSerializer().read( sb.toString());
            System.out.println( obj);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }





어쩌다 보니 간단한 XML 에디터 까지 개발해야 해서...
완전 삽질중. -_-;;
작년 9월부터 시작한걸 아직까지 잡고 있다.
뭐 중간에 캐나다고 갔다오고.. 하기 싫어서 다른 책도 보고 했지만..
이제 슬슬 끝이 보인듯 싶다. 역시 GUI는 번거롭다.

여튼 각설하고 XPATH로 원하는 노드를 찾은 후 XML 문서에 저장 하는 방법을 알아보자.

일단 처음엔 XPATH를 사용해야하기 때문에 XPathFactory 로 새 인스턴스를 만든 후, XPath도 새로 만들어 준다.

XPathFactory xpathFactory = XPathFactory.newInstance();
xpath = xpathFactory.newXPath();
xmlSource = new InputSource(Main.TESTSOURCE);
// 인풋 소스는 해당 xml 파일.

이렇게 해서 각각 인스턴스들을 만든후.
xpath 검색을 해서 엘리먼트에 할당한 다음에
이 엘리먼트에서 수정을 한 후.

Element element = (Element) xpath.evaluate(path, xmlSource, XPathConstants.NODE);
//xpath를 사용해서 검색.
Element root = (Element) element.getParentNode().getParentNode()
    .getParentNode().getParentNode();
//루트는 새로 문서를 저장할 때 필요하기 때문에 필요하다. 나 같은 경우에는 xpath를 사용해서 검색한 깊이가 4단계 이기 때문에 4번째 위의 부모 노드가 루트 노드이다.
Element feature = (Element) element.getChildNodes().item(3);
feature.setTextContent("원하는 데이터");
//해당 노드를 얻어 와서 setTextContent를 사용해서 값을 할당


이렇게 하면 일단 메모리에 올라와 있는 element 들은 다 수정이 되어 있다.
그 다음에는 TransformerFactory 를 사용해서 문서를 다시 저장해 주어야 한다.

TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
DOMSource source = new DOMSource(root);
StreamResult result = new StreamResult(new File(Main.TESTSOURCE));
transformer.transform(source, result);


DOM 객체를 수정, 추가, 제거하는 것은 모두 메모리상에서 이루어지고 이것을 영구 보본하기 위해서는 파일의 형태로 XML 문서로 저장해야 한다.

다양한 방법으로 문서를 저장 할 수 있지만 위의 방법은 XSLT변환기를 사용하여 파일로 저장한다.

XPATH 로 검색하면.

속성값은 정확히 일치 하는것을 찾을 수 있고.
내용값은 검색어를 포함한 것을 찾을 수 있다.

예제 추가는 나중에 ㅋ

XML 문서를 검색해야 하는 일이 생겼다.

Digester 와 Lucene 을 사용해서 파싱하고, 색인 만들고, 검색하는 예제.

http://www.ibm.com/developerworks/web/library/j-lucene/


2003년 글이네 -_-; 잘 될려나 몰라..

간단한 XML 에디터를 만들어 가고 있는중에...
XPATH를 사용하면 훨씬더 쉽게 검색할 수 있다는 사실을 발견...

OTL 그럼 전에 캐 삽질 DOM 트리 검색은....0_0?
XPATH로 고치는거 고고싱? ㅠ_ㅠ
귀찮다 OTL...일단 에디터 다 만들고 생각해 봐야지 ..

사용자 삽입 이미지

TreeViewer로 구현


마음 같아서야 이클립스에 포함된 XML 에디터 처럼 만들고 싶지만..
아직 허접해서 -_-;;

저기 펼쳐져 있는 트리중에 사진자료에 새로운 사진 노드를 추가하는 것부터 시작..

XPATH를 사용해서 할려고 하기때문에.. 대 난관에 봉착했지만..

다행이 해결 -ㅁ-v

일단 추가는 됬으니 자질구레한거 정리해서 하면 될것 같다..

이제그만 각설하고 소스를 살펴보면

일단 주요 내용을 살펴보면

문서를 파싱한다.
XPath로 객채를 만든다.
XPath표현식을 작성한다.
검색한 결과를 object로 저장하고.
여기에 새 엘리멘트를 추가한다.
그리고 최종적으로 메모리에 올라가 있는 돔 을 파일에 쓴다.


DocumentBuilderFactory domFactory = DocumentBuilderFactory
    .newInstance();

  //domFactory.setNamespaceAware(true); // never forget this!

  DocumentBuilder builder = domFactory.newDocumentBuilder();

  Document doc = builder.parse("VirusInformation.xml");

  XPathFactory factory = XPathFactory.newInstance();

  XPath xpath = factory.newXPath();

  XPathExpression expr = xpath.compile("//작물[@종류='채소']/병해/채소[@종류='오이']/병[@병명='노균병']/사진자료");

  Object result = expr.evaluate(doc, XPathConstants.NODESET);

  NodeList nodes = (NodeList) result;
 
  for (int i = 0; i < nodes.getLength(); i++) {
   Element e = (Element)nodes.item(i);
   Element newElement = doc.createElement("사진");
   Text objText = doc.createTextNode("사진 추가 연습");
   newElement.appendChild(objText);
   newElement.setAttribute("파일명", "1300.jpg");
   e.appendChild(newElement);
   System.out.println(nodes.item(i));
  }
 


  // 메모리에 추가한후 파일에 다시 쓰기.
 
  TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer;
  try {
   transformer = tFactory.newTransformer();
   transformer.transform(new DOMSource(doc), new StreamResult(new FileOutputStream("VirusInformation.xml")));
  } catch (TransformerConfigurationException e) {
   e.printStackTrace();
  } catch (TransformerException e) {
   e.printStackTrace();
  }



이런 형식으로?
아 오전내내 삽질해서 성공했구나. OTL

[Xercer 파서 다운로드] (자바용)

위에 사이트로 들어 간후 Xerces-J-bin.2.9.0.zip 파일을 다운로드 해서 압축을 푼다.
압축을 풀면 5개의 여러가지 폴더와 파일과 5개의 .jar 파일을 볼 수 있다.
이중에서 xercesImpl.jar, xml-apis.jar 파일을
자바가 설치된 폴더의 /jre/lib/ext 폴더에 압축을 푼다.
ex: C:\Program Files\Java\jdk1.6.0\jre\lib\ext
그 후  

XMLReader reader
       = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

이러한 형식으로 사용 하면 된다.