2012년 1월 2일 월요일

XmlPullParser.nextText()를 주의합시다.

[이 글은 달빅 팀의 Jesse Wilson이 작성한 것입니다. -Tim Bray] (Watch out for XmlPullParser.nextText()의 번역입니다.)



안드로이드에서 XML 파싱에 XmlPullParser를 사용하는 것이 효과적이고 유지보수 가능한 방법입니다. 역사적으로 안드로이드엔 이 인터페이스의 두가지 구현이 있습니다.

KXmlParser, XmlPullParserFactory.newPullParser()를 통합니다.
ExpatPullParser, Xml.newPullParser()를 통합니다.

Xml.newPullPaser()의 구현은 nextText()를 호출 시 문서에 명시한 END_TAG로의 진입을 항상하지 못하는 버그가 있습니다. 그래서 몇몇 앱에서 추가적인 next()nextTag()를 호출해서 이를 회피하고 있습니다.


 public void parseXml(Reader reader)
            throws XmlPullParserException, IOException {
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(reader);

        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, null, "menu");
        while (parser.nextTag() == XmlPullParser.START_TAG) {
            parser.require(XmlPullParser.START_TAG, null, "item");
            String itemText = parser.nextText();
            parser.nextTag(); // this call shouldn’t be necessary!
            parser.require(XmlPullParser.END_TAG, null, "item");
            System.out.println("menu option: " + itemText);
        }
        parser.require(XmlPullParser.END_TAG, null, "menu");
    }

    public static void main(String[] args) throws Exception {
        new Menu().parseXml(new StringReader("<?xml version='1.0'?>"
                + "<menu>"
                + "  <item>Waffles</item>"
                + "  <item>Coffee</item>"
                + "</menu>"));
    }

아이스크림 샌드위치에서 우리는 KXmlParser를 리턴하도록 Xml.newPullParser()를 변경했고 ExpatPullParser 클래스를 삭제했습니다. 이 변경은 nextTag() 버그를 고치죠. 불행히도 현재 버그를 회피하던 앱들이 아이스크림 샌드위치에서 망가질 수 있습니다.

org.xmlpull.v1.XmlPullParserException: expected: END_TAG {null}item (position:START_TAG <item>@1:37 in java.io.StringReader@40442fa8) 
     at org.kxml2.io.KXmlParser.require(KXmlParser.java:2046)
     at com.publicobject.waffles.Menu.parseXml(Menu.java:25)
 at com.publicobject.waffles.Menu.main(Menu.java:32)

이 수정된 코드는 현재 위치가 END_TAG가 아닐 경우에만 nextText ()호출 후 nextTag()를 호출합니다.

while (parser.nextTag() == XmlPullParser.START_TAG) {
      parser.require(XmlPullParser.START_TAG, null, "item");
      String itemText = parser.nextText();
      if (parser.getEventType() != XmlPullParser.END_TAG) {
          parser.nextTag();
      }
      parser.require(XmlPullParser.END_TAG, null, "item");
      System.out.println("menu option: " + itemText);
  }

위의 코드는 모든 릴리즈에서 XML을 정확히 파싱합니다. 만약 당신의 앱에서 nextText()가 널리 쓰이고 있다면 nextText()를 호출 곳에 이 헬퍼 메서드를 이용하세요.

private String safeNextText(XmlPullParser parser)
          throws XmlPullParserException, IOException {
      String result = parser.nextText();
      if (parser.getEventType() != XmlPullParser.END_TAG) {
          parser.nextTag();
      }
      return result;
  }

단일한 XmlPullParser로 간 점이 유지보수를 단순하게 만들고 우리가 더 많은 에너지를 성능 향상에 쓸 수 있게 합니다.

댓글 없음:

댓글 쓰기