안드로이드 XML 파서. (Android XML Parser)

2017. 6. 21. 16:01


1. XML (eXtensible Markup Language)

XML(eXtensible Markup Language)은 [W3C]에서 개발된, 사람과 기계가 모두 읽을 수 있는 형식(human-readable, machine-readable)으로 데이터를 기술할 수 있도록, 데이터를 문서화하는 규칙을 정의한 마크업 언어(markup language)입니다.


XML이 만들어진 가장 큰 목적은 인터넷을 통한 데이터 교환 시, 단순함(simplicity)범용성(generality), 그리고 손쉬운 사용성(usability)을 제공하는 것입니다. 특히, XML 자체의 텍스트를 인코딩할 때 기본적으로 유니코드(unicode)를 사용하기 때문에, 국가 간 언어-인코딩(language-encoding) 방식의 차이로 인한 문제를 근본적으로 예방할 수 있습니다.

2. XML 파서(Parser)

XML이 W3C에서 발표되고, 널리 사용되면서, XML을 처리하는 많은 API들이 개발되었습니다. 사실 XML 파서(Parser)를 만드는 작업은, 그리고 더 나아가 범용적으로 사용될 수준으로 잘 만드는 것은, 웬만큼 숙련된 소프트웨어 엔지니어가 아니라면 쉽지 않은 작업이며 시간이 많이 소요되는 일이기 때문에, 대부분의 경우, 누군가 만들어놓은 XML 파서(Parser)를 사용할 수 밖에 없습니다. 하지만 다행히도 다양한 플랫폼과 프로그래밍 언어를 지원하는 API들이 존재하므로, 자신이 만드는 프로그램에서 XML을 지원하는 것에 대한 문제는 크게 고민할 필요가 없습니다.

2.1 XML 파서의 유형 (DOM and SAX)

일반적으로 (XML 파서 뿐만아니라) 문서에서 데이터를 파싱하는 파서(Parser)가 동작하는 방식은 크게 두 가지로 나뉩니다. "문서 전체를 파싱한 결과를 메모리에 로드하여 문서의 내용과 1:1 매핑을 이루는 객체(Object)로 생성, 유지 및 탐색하는 방식""문서를 하나의 스트림으로 취급하여 문서의 처음부터 끝까지 단 한번의 탐색 과정만 거치면서, 유효한 요소가 식별될 때마다 파서(Parser)의 외부에 이벤트로 전달하여 식별된 요소에 대한 처리를 파서(Parser)의 외부에 일임하는 방식"입니다.


XML 파서의 유형에서는 전자를 DOM(Document Object Model)이라고 하고, 후자를 SAX(Simple API for XML)라고 합니다.

DOM(Document Object Model)과 SAX(Simple API for XML) 방식 차이점



2.2 DOM (Document Object Model)

앞서 간단히 언급했듯이, DOM(Document Object Model)은 XML 문서를 파싱한 다음, 그 결과를 XML 문서와 1:1로 매핑되는 객체(Object)로 생성하여 관리하는 방식입니다. DOM의 동작 방식에서의 핵심은, 파서(Parser) 자체에서 문서의 전체 내용을 읽어들인 다음, XML 문서의 구조를 그대로 반영하는 트리(Tree) 구조의 객체(Object)를 구성하는 것입니다. 이 때, 파서(Parser)가 문서의 처음부터 끝까지 파싱하여 최종 결과물인 객체(Object)를 생성하여 리턴하기 전까지는 어떠한 이벤트도 발생하지 않습니다.


일단 DOM 객체(Object)가 생성되고 나면, DOM 객체에서 제공하는 API를 통해, 루트(Root) 노드 식별, 자식(Child)과 부모(Parent) 또는 형제(Sibling) 노드 탐색 등의 기능을 사용할 수 있습니다. 또한 탐색된 노드(Node)에 대한 이름(Name), 텍스트(Text), 속성(Attribute) 값 등도 API를 통해 쉽게 접근할 수 있습니다.


DOM 객체에서 자유로운 탐색이 가능한 이유는 파싱 과정에서 식별된 모든 요소에 대한 정보가 메모리에 로드되기 때문인데요, 이렇게 모든 요소들을 메모리에 로드하게 되면 XML 문서에 새로운 노드를 추가하거나, 기존 노드를 다른 위치로 이동 또는 삭제하는 기능을 쉽게 구현할 수 있습니다. 그리고 대부분의 DOM API에서는 XML 문서를 객체(Object)로 만드는 파서(Parser) 기능 뿐만 아니라, 객체(Object)를 XML 문서로 변환해주는 생성(Generator) 기능을 제공하므로, 수정된 DOM 객체를 XML로 손쉽게 문서화할 수 있습니다.


하지만 "모든 요소의 메모리 로드"라는 특징은, DOM의 가장 큰 단점이기도 합니다. 특히, XML 문서에 포함된 요소의 갯수가 많으면, DOM 객체에 할당되는 메모리도 그 만큼 늘어나기 때문에 메모리 리소스 부족 문제가 발생할 수 있습니다.


DOM 방식의 XML 파서는 주로 XML 문서 내용에 대한 탐색이 빈번한 경우 또는 XML 문서의 내용이 빈번하게 수정되는 경우에 사용됩니다.

2.3 SAX (Simple API for XML)

SAX(Simple API for XML)는 XML 문서를 단-방향 스트림으로 취급하여 문서의 처음부터 끝까지 읽어들이는 중에, 유효한 요소(태그, 속성, 텍스트 등)가 식별되면 그 이벤트를 파서의 외부로 전달하여 요소에 대한 처리를 수행하는 방식입니다.


SAX 방식에서는, XML 파서(Parser)가 XML 문서의 구조 또는 요소에 대한 탐색에 전혀 관여하지 않고, 그 결과물에 대한 처리를 파서(Parser)의 외부, 즉, 파서(Parser)를 사용하는 사용자에게 일임합니다. 식별된 요소 값을 어떻게 사용 및 관리할 것인지에 대한 결정을 완벽하게 개발자의 몫으로 남겨두는 방식인 것이죠. 그래서 SAX 방식의 파서(Parser) 실행이 완료되어도, DOM 방식과는 다르게, 개발자가 의도적으로 남긴 정보 외의 어떠한 정보도 메모리에 남지 않습니다.


SAX 방식의 파서를 사용하면 DOM 방식에서 얻을 수 있는 장점인 XML 노드의 자유로운 탐색이 힘들어집니다. DOM 객체처럼 XML 문서를 반영하는 트리 구조 객체(Object)도 생성하지 않고, XML 문서를 단-방향 스트림으로 취급하기 때문에 원하는 요소로의 이동이 여의치 않기 때문입니다. 그리고 같은 이유로, XML 요소의 이동, 추가, 삭제 등의 수정 작업도 쉽지 않습니다. 결국 노드 탐색, 이동, 추가, 삭제 등의 작업을 위해서는 SAX 파서에서 전달되는 요소 식별 이벤트로부터 DOM 객체와 유사한 구조의 데이터를 직접 구조화해야 하는데, 그런 상황이라면 차라리 DOM 방식의 파서(Parser)를 사용하는 것이 훨씬 효율적입니다.


하지만 역시, "SAX"의 "S"가 "Simple"의 앞 글자인 만큼, 파서(Parser)의 동작 방식이 매우 간단하다는 장점이 있습니다. XML 문서를 파싱하는 과정에서 식별된 요소 중 원하는 정보만 선별적으로 사용할 수 있기 때문에, XML 문서의 구조가 중요하지 않은 경우라면 XML 문서에서 원하는 정보를 찾아서 사용하기가 훨씬 간단합니다. 특히 XML 문서의 구조가 간단하거나 동일한 요소들이 반복되는 경우, 매우 효율적으로 사용할 수 있습니다. 그리고 DOM 방식에 비해 훨씬 적은 메모리를 사용한다는 장점이 있습니다.

2.4 DOM vs SAX

아래의 표는 DOM 방식과 SAX 방식의 특징 및 차이점을 비교한 것입니다.

항목 DOM(Document Object Model) SAX(Simple API for XML)
파서의 동작 결과물로 트리 구조의 객체(Object) 리턴.
파싱 시작 후 완료될 때까지 대기.
DOM 객체(Object)에서 원하는 요소 탐색.
XML 문서를 단-방향 스트림으로 처리.
파싱 중 유효한 요소가 식별되면 이벤트로 전달.
식별된 요소에서 필요한 값을 개발자가 직접 저장.
파서의 장점 XML 노드 탐색, 추가, 수정, 삭제 작업이 용이함.
XML 문서의 생성이 편함.
동작 방식이 간단.
선별적 요소 식별 시 속도가 빠름.
파서(Parser)가 사용하는 메모리가 적음.
파서의 단점 많은 양의 메모리 사용.
단순 요소 값 식별 시 속도 느림.
XML 노드 탐색, 추가, 수정, 삭제 작업이 쉽지 않음.
XML 문서의 생성은 개발자의 몫.
파서의 사용 XML 문서 탐색이 빈번하게 발생하는 경우.
XML 문서 구조가 자주 변경되는 경우.
XML 문서의 구조가 아닌 요소의 값이 중요한 경우.
XML 문서의 구조가 간단하거나 동일한 요소가 반복되는 경우.

3. 안드로이드에서 XML 파서 사용하기

3.1 XmlPullParser 인터페이스

안드로이드에서 기본적으로 지원되는 XML 파서는 XmlPullParser 입니다. XmlPullParser는 안드로이드 SDK 내에 인터페이스로 정의되어 있으며, [XMLPULL V1 API]에서 정의한 규칙들과 API들을 포함하고 있습니다. 그리고 XmlPullParser는 SAX(Simple API for XML) 방식으로 동작합니다.

XmlPullParser


XmlPullParser가 인터페이스로 정의되어 있다는 것은, XmlPullParser에 XML 파싱과 관련된 함수가 정의되어 있다는 것이고, XmlPullParser의 참조를 통해 관련 함수를 사용할 수 있다는 의미입니다. 하지만 이는 곧, XML 파싱을 위한 함수의 실질적인 구현은 XmlPullParser에 포함되어 있지 않다는 것을 의미하기도 합니다. 다시 한번 언급하지만, XmlPullParser는 인터페이스로 정의되어 있으니까요.


그렇다면 XML 파서의 기능을 구현하기 위해, 개발자가 XmlPullParser 인터페이스를 상속받은 클래스를 만들고, 각 함수의 기능을 하나하나 전부 구현해야 하는 걸까요?
아니, 그렇지 않습니다. 분명, XmlPullParser 인터페이스를 상속받아 각 함수의 기능을 구현한 클래스가 필요하지만, 그것을 개발자가 직접 만들 필요는 없습니다. 이미 안드로이드에서 그러한 클래스를 만들어서 제공하고 있기 때문이죠.


안드로이드에서 제공되는, XmlPullParser 인터페이스의 XML 파싱 기능이 구현된 클래스는 크게 두 가지가 있습니다. 바로 XmlPullParserFactory.newPullParser() 함수를 통해 생성가능한 KXmlParser와, Xml.newPullParser() 함수를 통해 생성할 수 있는 ExpatPullParser 입니다.

KXmlParser 와 ExpatPullParser


하지만 KXmlParser나 ExpatPullParser의 이름 또는 내부 구현 방식에 대해서는 크게 신경쓰지 않아도 됩니다. 왜냐하면 두 가지 클래스 모두 XmlPullParser를 implements하고 있고, 개발자가 필요한 XML 파싱 관련 함수는 XmlPullParser를 통해 호출할 수 있으니까요.


두 가지 파서 중 어떤 것을 사용할지는 어쨌든 최종적으로 개발자가 결정할 문제이지만, 어떤 것을 사용해도 XML 파일을 다루는데는 큰 차이가 없습니다. 그냥 둘 중 하나를 선택하면 됩니다. 단, 인스턴스를 만들고, 생성된 인스턴스 참조를 XmlPullParser 인터페이스 타입으로 획득하는 과정은 XML 파서 종류에 따라 다르기 때문에, 각 파서에 맞는 초기화 방법을 사용해야 합니다.

3.2 XML 파서 초기화하기.

안드로이드에서 제공하는 두 가지 종류의 파서에 대한 소개 문장에서 잠시 언급했듯이, 각 파서는 그 자신의 인스턴스를 생성하는 역할을 담당하는 특정 클래스의 함수 호출을 통해 생성됩니다. 즉, KXmlParser는 XmlPullParserFactory.newPullParser() 함수를 통해 생성되고, ExpatPullParser는 Xml.newPullParser() 함수 호출을 통해 생성됩니다.


아래 코드는 XmlPullParserFactory를 통해 XmlPullParser의 인스턴스를 만드는 방법입니다.

[STEP-1.1] XML 파서 생성 - KXmlParser 생성하는 방법.
    try {
        XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
        XmlPullParser parser = parserFactory.newPullParser() ;
    } catch () {
        ...
    }

다음은 Xml 클래스의 newPullParser() 함수를 통해 XmlPullParser 인스턴스를 만드는 방법입니다.

[STEP-1.2] XML 파서 생성 - ExpatPullParser 생성하는 방법.
    try {
        XmlPullParser parser = Xml.newPullParser() ;
    } catch () {
        ...
    }

두 가지 파서의 초기화 방법이 다르긴 하지만, 초기화 과정을 통해 얻어지는 XML 파서의 인스턴스는 XmlPullParser 타입으로 참조가 가능합니다. 그러므로 XML 파서 생성 과정 외에, 실질적인 XML 데이터 파싱 과정은 파서 종류에 따른 차이가 없습니다.

3.3 XML 파서에 입력 스트림 지정.

XML 파서에는 openFile()과 같은, 직접적으로 XML 파일을 여는 함수가 존재하지 않습니다. 대신 파서가 처리할 입력 스트림을 지정하는 setInput() 함수가 존재하며, setInput() 함수를 사용하여 파서 외부에서 오픈된 입력 스트림을 처리하도록 만들어져 있습니다.

리턴 타입 함수 설명
void setInput(Reader in) reader를 파서(Parser)의 입력 소스로 지정하고 파서 초기화
void setInput(InputStream inputStream, String inputEncoding) inputStream을 파서(Parser)의 입력 소스로 지정.

다음과 같이, 파일 입력 스트림을 열고, setInput() 함수를 통해 스트림에 대한 참조를 전달할 수 있습니다.

[STEP-2] 파일 열기 - 입력 스트림과 setInput() 함수를 사용하여 XML 파일 열기.
    FileInputStream fis = new FileInputStream("file.xml") ;
    parser.setInput(fis, null) ;

한 가지 주의할 점은 setInput() 함수를 사용하여 입력 스트림을 지정했다고 하더라도, 당장 XML 데이터가 파싱되는 것은 아니라는 것입니다. 실질적인 XML 데이터의 파싱은, 파일의 시작부터 파일의 끝까지 파일의 내용을 탐색하면서 XML 요소(TAG, TEXT 등)를 찾는 방식으로 수행됩니다.

3.4 XML 데이터 파싱.

XML 파서를 초기화하고 XML 파일을 파서의 입력 스트림으로 지정했다면, 이제 실질적인 XML 데이터를 파싱할 수 있습니다.


앞 서 간단히 언급했듯이, XML 파싱을 위한 데이터 탐색 과정은 파일의 처음부터 시작하여 파일의 끝을 만날 때까지 순차적으로 실행됩니다. XML 파일의 탐색은 XmlPullParser의 next() 함수를 통해 실행되며, next() 함수 실행 중 XML 요소를 식별하게 되면, next() 함수는 탐색을 중지하고 식별된 XML 요소를 이벤트 타입으로 리턴합니다.


이벤트 타입으로 리턴되는 XML 요소는 태그(TAG), 텍스트(TEXT) 뿐만 아니라 XML 파일의 시작과 끝도 포함되므로, 개발자는 항상 이 이벤트 타입을 검사하여 파싱된 데이터를 사용하거나, 파싱을 계속 진행할지 여부를 결정해야 합니다.


XmlPullParser에서 이벤트 타입으로 식별되는 XML 요소는 아래와 같은 것들이 있습니다.

식별 XML 요소 설명
START_DOCUMENT XML 문서의 시작. 파싱(Parsing)의 시작을 알림.
START_TAG XML 시작 태그. (ex. <TAG>).
TEXT XML에 포함된 텍스트. 기본적으로 시작 태그와 종료 태그 사이의 문자열. (ex. <TAG>TEXT</TAG>))
END_TAG XML 종료 태그. (ex. </TAG>).
END_DOCUMENT XML 문서의 끝. 파싱(Parsing)의 끝을 알림.

실제 XML 문서의 내용이 코드 상에서 어떤 XML 요소에 매핑되는지는 아래 그림을 통해 확인할 수 있습니다.

XML 문서에서 식별된 XML 요소


자, 그럼 이제, 위에서 예로 든 XML 데이터를 파싱하여 NO 값 1, NAME 값 "ppotta"를 획득하는 코드를 작성해 보겠습니다. 단, 아래의 코드는 XML 파싱 과정의 이해를 돕기 위해, 순차적으로 실행되는 직관적인 방법을 사용하고 있는데, 이는 다양한 구조의 XML 데이터를 처리하기엔 활용성이 제한적인 방법입니다. 그러므로 참고 용도로만 살펴보시기 바랍니다.

[STEP-3.1] XML 파싱 - 순차적 XML 데이터 파싱.
    int no = -1 ;
    String name = null ;
    int eventType ;

    // XML 파일의 시작.
    eventType = parser.getEventType() ;
    if (eventType == XmlPullParser.START_DOCUMENT) {
        eventType = parser.next() ; // <NO> 시작 태그 파싱.
        if (eventType == XmlPullParser.START_TAG && parser.getName().equals("NO")) {
            eventType = parser.next() ; // <NO> 태그의 TEXT(1) 파싱.
            if (eventType == XmlPullParser.TEXT) {
                String text = parser.getText() ;
                no = Integer.parseInt(text) ;
            }
            eventType = parser.next() ; // </NO> 종료 태그 파싱.
        }

        eventType = parser.next() ; // <NAME> 시작 태그 파싱.
        if (eventType == XmlPullParser.START_TAG && parser.getName().equals("NAME")) {
            eventType = parser.next() ; // <NAME> 태그의 TEXT(ppotta) 파싱.
            if (eventType == XmlPullParser.TEXT) {
                String name = parser.getText() ;
            }
            eventType = parser.next() ; // </NAME> 종료 태그 파싱.
        }

        eventType = parser.next() ;
        if (eventType != XmlPullParser.END_DOCUMENT) {
            // ERROR : more data.
        }
    }

    if (no == -1 || name == null) {
        // ERROR : XML is invalid.
    }

예제 코드를 보면, 최초 getEventType() 함수를 통해 START_DOCUMENT 상태인지를 확인한 다음, 파일에 저장된 XML 요소의 갯수만큼 next() 함수를 호출하면서 시작 태그(, )와 종료 태그(, ) 사이에 있는 텍스트(1, ppotta)를 추출하도록 구현되어 있습니다.


앞서 잠시 언급했듯이, 이러한 방법은 다양한 상황에서 범용적으로 사용하기엔 제약이 있는데, 특히 파싱해야 할 XML 요소의 갯수가 많거나 XML 요소들이 반복되는 경우에는 사용하기 적합하지 않습니다.


그래서 일반적으로 XML 데이터를 파싱할 때는 순차적 방법이 아닌, 루프를 돌면서 XML 요소를 파싱하는 방법을 사용합니다. 기본적인 코드 구조는 아래와 같습니다. (참고로 아래의 코드 구조는 [안드로이드 개발 참조문서. XmlPullParser 항목]에서도 확인할 수 있습니다.)

    int eventType = parser.getEventType() ;

    while (eventType != XmlPullParser.END_DOCUMENT) {
        if (eventType == XmlPullParser.START_DOCUMENT) {
            // XML 데이터 시작
        } else if (eventType == XmlPullParser.START_TAG) {
            // 시작 태그가 파싱. "<TAG>"
            System.out.println("Start tag "+xpp.getName());
        } else if (eventType == XmlPullParser.END_TAG) {
            // 종료 태그가 파싱. "</TAG>"
            System.out.println("End tag "+xpp.getName());
        } else if (eventType == XmlPullParser.TEXT) {
            // 시작 태그와 종료 태그 사이의 텍스트. "<TAG>TEXT</TAG>"
            System.out.println("Text "+xpp.getText());
        }
        eventType = parser.next();
    }

그럼 이제 루프를 돌면서 예제 XML 데이터를 파싱하는 코드를 살펴보겠습니다.

[STEP-3.2] XML 파싱 - 루프를 돌며 XML 데이터 파싱.
    final int STEP_NONE = 0 ;
    final int STEP_NO = 1 ;
    final int STEP_NAME = 2 ;

    int step = STEP_NONE ;
    int no = -1 ;
    String name = null ;
    int eventType = parser.getEventType() ;

    while (eventType != XmlPullParser.END_DOCUMENT) {
        if (eventType == XmlPullParser.START_DOCUMENT) {
            // XML 데이터 시작
        } else if (eventType == XmlPullParser.START_TAG) {
            String startTag = parser.getName() ;
            if (startTag.equals("NO")) {
                step = STEP_NO ;
            } else if (startTag.equals("NAME")) {
                step = STEP_NAME ;
            } else {
                step = STEP_NONE ;
            }
        } else if (eventType == XmlPullParser.END_TAG) {
            String endTag = parser.getName() ;
            if ((endTag.equals("NO") && step != STEP_NO) ||
                 endTag.equals("NAME") && step != STEP_NAME)
            {
                // TODO : error.
            }
            step = STEP_NONE ;
        } else if (eventType == XmlPullParser.TEXT) {
            String text = parser.getText() ;
            if (step == STEP_NO) {
                no = Integer.parseInt(text) ;
            } else if (step == STEP_NAME) {
                name = text ;
            }
        }

        eventType = parser.next();
    }

    if (no == -1 || name == null) {
        // ERROR : XML is invalid.
    }

루프를 이용하는 코드의 핵심은, while 루프 내에서 next() 함수 호출로 파싱되는 XML 요소가 무엇인지 판단하고, 원하는 요소(TEXT) 값이 파싱되면 그 값을 복사하여 사용하는 것입니다. 이 때, TEXT 값이 파싱되면, 해당 TEXT가 어떤 태그의 TEXT 값인지를 판단하기 위해 step이라는 변수를 사용하여 현재 파싱 중인 태그를 검사하도록 만들었습니다.


하지만 TEXT의 태그를 판단하기 위해, step 변수에 현재 파싱된 시작 태그를 저장하는 방법은 여러 방법 중 하나일 뿐이므로, 반드시 이러한 방법을 사용할 필요는 없습니다. 또 다른 방법에 대해서는 [안드로이드 개발 Training - Parsing XML Data]에서 확인할 수 있으며, 앞으로 작성될 여러 예제를 통해 소개하도록 하겠습니다.

4. XML 파서(Parser) 예제 전체 코드

위에서 소개한 예제의 전체 코드는 아래와 같습니다.

    try {
        final int STEP_NONE = 0 ;
        final int STEP_NO = 1 ;
        final int STEP_NAME = 2 ;

        XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
        XmlPullParser parser = parserFactory.newPullParser() ;
        FileInputStream fis = new FileInputStream("file.xml") ;
        int step = STEP_NONE ;
        int no = -1 ;
        String name = null ;
        
        parser.setInput(fis, null) ;

        int eventType = parser.getEventType() ;
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_DOCUMENT) {
                // XML 데이터 시작
            } else if (eventType == XmlPullParser.START_TAG) {
                String startTag = parser.getName() ;
                if (startTag.equals("NO")) {
                    step = STEP_NO ;
                } else if (startTag.equals("NAME")) {
                    step = STEP_NAME ;
                } else {
                    step = STEP_NONE ;
                }
            } else if (eventType == XmlPullParser.END_TAG) {
                String endTag = parser.getName() ;
                if ((endTag.equals("NO") && step != STEP_NO) ||
                     endTag.equals("NAME") && step != STEP_NAME))
                {
                    // TODO : error.
                }
            } else if (eventType == XmlPullParser.TEXT) {
                String text = parser.getText() ;
                if (step == STEP_NO) {
                    no = Integer.parseInt(text) ;
                } else if (step == STEP_NAME) {
                    name = text ;
                }
            }

            eventType = parser.next();
        }

        if (no == -1 || name == null) {
            // ERROR : XML is invalid.
        }
    } catch (Exceptin e) {
        e.printStackTrace() ;
    }

5. XML 파서 사용 시 주의 사항.

XML 파서를 사용할 때, 개발자들이 주의해야 할 몇 가지 내용들이 있습니다.


  • 태그를 파싱했을 때 태그의 이름을 가져올 때는 getName(), 텍스트를 파싱했을 때 텍스트의 내용을 가져올 때는 getText()를 사용.
  • 시작 태그와 종료 태그 사이에 빈 문자열만 있는 경우에도 TEXT 이벤트 실행.
  • 시작 태그와 종료 태그의 바깥에 공백, 줄바꿈에도 TEXT 이벤트 실행.

5.1 태그의 이름은 getName(), 텍스트의 내용은 getText() 를 사용.

XML 파서가 XML 데이터를 파싱하다가, 태그를 파싱하면 START_TAG 이벤트를, 태그 사이의 텍스트를 파싱하면 TEXT 이벤트를 리턴합니다. 이 때 파싱된 XML 요소의 이름을 가져오는 함수가 각각 존재하는데요, 태그(START_TAG, END_TAG)를 가져올 때는 getName() 함수를 사용하고, 텍스트(TEXT)의 내용을 가져올 때는 getText() 함수를 사용해야 합니다. (참고로 시작 태그에 존재하는 속성의 이름을 가져오려면 getAttributeName(), 속성의 값은 getAttributeValue() 함수를 사용합니다.)

    <NAME firstname="true">ppotta</NAME>
    if (eventType == XmlPullParser.START_TAG) {
        String startTag = parser.getName() ; // "NAME" 리턴.
    } else if (eventType == XmlPullParser.TEXT) {
        String text = parser.getText() ; // "ppotta" 리턴.
    } else if (eventType == XmlPullParser.END_TAG)
        String endTag = parser.getName() ; // "NAME" 리턴.

5.2 시작 태그와 종료 태그 사이에 빈 문자열만 있는 경우에도 TEXT 이벤트 실행.

본문의 예제를 보면 알겠지만, XML 파서는 시작 태그와 종료 태그 사이에 존재하는 텍스트에 대해 TEXT 이벤트로 리턴합니다. 그런데 이 때 텍스트의 내용에 탭 또는 스페이스만 들어 있으면 TEXT 이벤트가 실행되지 않는 것으로 착각할 수 있는데, 텍스트가 빈 문자로 채워져 있어도 TEXT 로 파싱하여 이벤트를 리턴합니다. 물론, getText() 를 이용해 텍스트를 가져오면 탭 또는 스페이스 문자열이 들어 있는 것을 확인할 수 있습니다.

    <NAME>   </NAME>

하지만 태그 사이에 빈 문자열 조차 존재하지 않는다면, TEXT 이벤트는 아예 실행되지 않는다는 것을 주의해야 합니다.

    <NAME></NAME>     <!-- TEXT 이벤트 실행되지 않음. -->

5.3 시작 태그와 종료 태그의 바깥에 공백, 줄바꿈에도 TEXT 이벤트 실행.

TEXT 이벤트는 시작 태그와 종료 태그의 안쪽 문자열에만 해당되는 것이 아닙니다. 태그 밖에 문자열이 존재하는 경우에도 TEXT 이벤트는 실행됩니다. 아래의 경우 "</NO>"와 "<NAME>" 사이의 빈 공간에 대해서도 TEXT 이벤트가 실행되는 것입니다.

    <NO>1</NO>     <NAME>ppotta</NAME>

6. 참고.

.END.


ANDROID 프로그래밍/XML