IT TIP

SAX 파서를 사용하여 XML을 구문 분석하는 방법

itqueen 2020. 12. 9. 22:06
반응형

SAX 파서를 사용하여 XML을 구문 분석하는 방법


튜토리얼을 따르고 있습니다 .

훌륭하게 작동하지만 마지막 요소가있는 단일 문자열 대신 모든 문자열이있는 배열을 반환하고 싶습니다.

이 작업을 수행하는 방법에 대한 아이디어가 있습니까?


따라서 이와 같은 RSS 피드를 구문 분석하기 위해 XML 파서를 빌드하려고합니다.

<rss version="0.92">
<channel>
    <title>MyTitle</title>
    <link>http://myurl.com</link>
    <description>MyDescription</description>
    <lastBuildDate>SomeDate</lastBuildDate>
    <docs>http://someurl.com</docs>
    <language>SomeLanguage</language>

    <item>
        <title>TitleOne</title>
        <description><![CDATA[Some text.]]></description>
        <link>http://linktoarticle.com</link>
    </item>

    <item>
        <title>TitleTwo</title>
        <description><![CDATA[Some other text.]]></description>
        <link>http://linktoanotherarticle.com</link>
    </item>

</channel>
</rss>

이제 작업 할 수있는 두 가지 SAX 구현이 있습니다. org.xml.sax또는 android.sax구현 을 사용합니다 . 간단한 예제를 게시 한 후 둘 다의 장단점을 설명하겠습니다.

android.sax 구현

android.sax구현 부터 시작하겠습니다 .

먼저 RootElementElement개체를 사용하여 XML 구조를 정의해야 합니다.

어쨌든 나는 귀하의 데이터를 보유하는 POJO (Plain Old Java Objects)로 작업 할 것입니다. 여기에 필요한 POJO가 있습니다.

Channel.java

public class Channel implements Serializable {

    private Items items;
    private String title;
    private String link;
    private String description;
    private String lastBuildDate;
    private String docs;
    private String language;

    public Channel() {
        setItems(null);
        setTitle(null);
        // set every field to null in the constructor
    }

    public void setItems(Items items) {
        this.items = items;
    }

    public Items getItems() {
        return items;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
    // rest of the class looks similar so just setters and getters
}

이 클래스는 Serializable인터페이스를 구현 하므로 a에 Bundle넣고 작업을 수행 할 수 있습니다 .

이제 항목을 보관할 클래스가 필요합니다. 이 경우에는 ArrayList클래스 를 확장 할 것 입니다.

Items.java

public class Items extends ArrayList<Item> {

    public Items() {
        super();
    }

}

그게 우리 항목 컨테이너입니다. 이제 모든 단일 항목의 데이터를 보관할 클래스가 필요합니다.

Item.java

public class Item implements Serializable {

    private String title;
    private String description;
    private String link;

    public Item() {
        setTitle(null);
        setDescription(null);
        setLink(null);
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    // same as above.

}

예:

public class Example extends DefaultHandler {

    private Channel channel;
    private Items items;
    private Item item;

    public Example() {
        items = new Items();
    }

    public Channel parse(InputStream is) {
        RootElement root = new RootElement("rss");
        Element chanElement = root.getChild("channel");
        Element chanTitle = chanElement.getChild("title");
        Element chanLink = chanElement.getChild("link");
        Element chanDescription = chanElement.getChild("description");
        Element chanLastBuildDate = chanElement.getChild("lastBuildDate");
        Element chanDocs = chanElement.getChild("docs");
        Element chanLanguage = chanElement.getChild("language");

        Element chanItem = chanElement.getChild("item");
        Element itemTitle = chanItem.getChild("title");
        Element itemDescription = chanItem.getChild("description");
        Element itemLink = chanItem.getChild("link");

        chanElement.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                channel = new Channel();
            }
        });

        // Listen for the end of a text element and set the text as our
        // channel's title.
        chanTitle.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                channel.setTitle(body);
            }
        });

        // Same thing happens for the other elements of channel ex.

        // On every <item> tag occurrence we create a new Item object.
        chanItem.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                item = new Item();
            }
        });

        // On every </item> tag occurrence we add the current Item object
        // to the Items container.
        chanItem.setEndElementListener(new EndElementListener() {
            public void end() {
                items.add(item);
            }
        });

        itemTitle.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                item.setTitle(body);
            }
        });

        // and so on

        // here we actually parse the InputStream and return the resulting
        // Channel object.
        try {
            Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler());
            return channel;
        } catch (SAXException e) {
            // handle the exception
        } catch (IOException e) {
            // handle the exception
        }

        return null;
    }

}

보시다시피 매우 빠른 예입니다. android.saxSAX 구현 사용의 주요 이점은 구문 분석해야하는 XML 구조를 정의한 다음 적절한 요소에 이벤트 리스너를 추가 할 수 있다는 것입니다. 단점은 코드가 상당히 반복되고 부풀어 오른다는 것입니다.

org.xml.sax 구현

org.xml.saxSAX 핸들러 구현은 조금 다르다.

여기서는 XML 구조를 지정하거나 선언하지 않고 이벤트 만 수신합니다. 가장 널리 사용되는 이벤트는 다음과 같습니다.

  • 문서 시작
  • 문서 끝
  • 요소 시작
  • 요소 끝
  • 요소 시작과 요소 끝 사이의 문자

위의 Channel 객체를 사용한 핸들러 구현의 예는 다음과 같습니다.

public class ExampleHandler extends DefaultHandler {

    private Channel channel;
    private Items items;
    private Item item;
    private boolean inItem = false;

    private StringBuilder content;

    public ExampleHandler() {
        items = new Items();
        content = new StringBuilder();
    }

    public void startElement(String uri, String localName, String qName, 
            Attributes atts) throws SAXException {
        content = new StringBuilder();
        if(localName.equalsIgnoreCase("channel")) {
            channel = new Channel();
        } else if(localName.equalsIgnoreCase("item")) {
            inItem = true;
            item = new Item();
        }
    }

    public void endElement(String uri, String localName, String qName) 
            throws SAXException {
        if(localName.equalsIgnoreCase("title")) {
            if(inItem) {
                item.setTitle(content.toString());
            } else {
                channel.setTitle(content.toString());
            }
        } else if(localName.equalsIgnoreCase("link")) {
            if(inItem) {
                item.setLink(content.toString());
            } else {
                channel.setLink(content.toString());
            }
        } else if(localName.equalsIgnoreCase("description")) {
            if(inItem) {
                item.setDescription(content.toString());
            } else {
                channel.setDescription(content.toString());
            }
        } else if(localName.equalsIgnoreCase("lastBuildDate")) {
            channel.setLastBuildDate(content.toString());
        } else if(localName.equalsIgnoreCase("docs")) {
            channel.setDocs(content.toString());
        } else if(localName.equalsIgnoreCase("language")) {
            channel.setLanguage(content.toString());
        } else if(localName.equalsIgnoreCase("item")) {
            inItem = false;
            items.add(item);
        } else if(localName.equalsIgnoreCase("channel")) {
            channel.setItems(items);
        }
    }

    public void characters(char[] ch, int start, int length) 
            throws SAXException {
        content.append(ch, start, length);
    }

    public void endDocument() throws SAXException {
        // you can do something here for example send
        // the Channel object somewhere or whatever.
    }

}

이제 솔직히 말해서이 핸들러 구현이 android.sax하나에 비해 실제적인 이점을 말할 수는 없습니다 . 그러나 나는 지금까지 꽤 명백해야 할 단점을 말할 수 있습니다. startElement메소드 의 else if 문을 살펴보십시오 . 때문에 우리가 태그를 가지고 있다는 사실에 <title>, link그리고 description우리는 우리가 지금있는 XML 구조가 추적 할 수 있습니다. 우리가 발생하는 경우 즉 <item>시작 태그 우리가 설정 inItem하는 플래그를 true우리가 올바른 객체에 올바른 데이터를 매핑하도록 상기에 endElement있어서, 우리는 해당 플래그 설정 false우리가 발생하면 </item>태그. 해당 항목 태그가 완료되었음을 알리기 위해.

이 예에서는 관리하기가 매우 쉽지만 다른 수준에서 반복되는 태그를 사용하여 더 복잡한 구조를 구문 분석하는 것은 까다로워집니다. 예를 들어 현재 상태를 설정하기 위해 Enum을 사용하고 현재 위치를 확인하기 위해 많은 스위치 / 케이스 상태 메 니트를 사용해야합니다. 또는 더 우아한 솔루션은 태그 스택을 사용하는 일종의 태그 추적기입니다.


많은 문제에서 다른 목적을 위해 다른 종류의 xml 파일을 사용해야합니다. 나는 그 광대 함을 파악하고 내가이 모든 것을 필요로하는 것을 내 경험으로 말하려고하지 않을 것이다.

자바, 아마도 내가 가장 좋아하는 프로그래밍 언어 일 것이다. 또한이 사랑은 어떤 문제라도 해결할 수 있고 자전거를 떠 올릴 필요가 없다는 사실로 강화된다.

그래서 클라이언트가 원격으로 데이터베이스 서버에 항목을 만들 수 있도록 데이터베이스를 실행하는 클라이언트-서버 무리를 만들어야했습니다. 입력 데이터 등을 확인할 필요는 없지만 그게 아닙니다.

나는 일의 원칙으로 망설임없이 xml 파일 형태의 정보 전송을 선택했다. 다음 유형 중 :

<? xml version = "1.0" encoding = "UTF-8" standalone = "no"?> 
<doc> 
<id> 3 </ id> 
<fam> Ivanov </ fam> 
<name> Ivan </ name> 
<otc> I. </ otc> 
<dateb> 10-03-2005 </ dateb> 
<datep> 10-03-2005 </ datep> 
<datev> 10-03-2005 </ datev> 
<datebegin> 09-06-2009 </ datebegin> 
<dateend> 10-03-2005 </ dateend> 
<vdolid> 1 </ vdolid> 
<specid> 1 </ specid> 
<klavid> 1 </ klavid> 
<stav> 2.0 </ stav> 
<progid> 1 </ progid> 
</ doc> 

의사 기관에 대한 정보라는 것을 제외하고는 더 읽기 쉽게 만드십시오. 성, 이름, 고유 ID 등. 일반적으로 데이터 시리즈입니다. 이 파일은 안전하게 서버 측에 도착한 다음 파일 구문 분석을 시작합니다.

두 가지 구문 분석 옵션 (SAX 대 DOM) 중에서 나는 그가 더 밝게 일한다는 사실에 대한 SAX보기를 선택했고, 그는 내가 손에 들었던 첫 번째 사람이었습니다. :)

그래서. 아시다시피, 파서로 성공적으로 작업하려면 필요한 DefaultHandler 메서드를 재정의해야합니다. 시작하려면 필요한 패키지를 연결하십시오.

import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax. *; 

이제 파서 작성을 시작할 수 있습니다.

public class SAXPars extends DefaultHandler {
   ... 
} 

startDocument () 메서드부터 시작하겠습니다. 이름에서 알 수 있듯이 그는 문서의 시작 이벤트에 반응합니다. 여기에서 메모리 할당과 같은 다양한 작업을 중단하거나 값을 재설정 할 수 있지만, 우리의 예는 매우 간단하므로 적절한 메시지의 작업 시작을 표시하기 만하면됩니다.

Override 
public void startDocument () throws SAXException {
   System.out.println ("Start parse XML ..."); 
} 

다음. 파서는 문서를 통해 구조 요소를 충족합니다. startElement () 메서드를 시작합니다. 그리고 실제로 그의 모습은 다음과 같습니다 : startElement (String namespaceURI, String localName, String qName, Attributes atts). 여기서 namespaceURI-네임 스페이스, localName-요소의 로컬 이름, qName-로컬 이름과 네임 스페이스 (콜론으로 구분)의 조합 및 atts-이 요소의 속성. 이 경우 모두 간단합니다. qName'om을 사용하고 thisElement의 일부 서비스 라인에 던지는 것으로 충분합니다. 따라서 우리는 현재 우리가있는 요소를 표시합니다.

@Override 
public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
   thisElement = qName; 
} 

다음으로 회의 항목은 그 의미를 얻습니다. 여기에는 메소드 문자 ()가 포함됩니다. 형식은 문자 (char [] ch, int start, int length)입니다. 여기 모든 것이 명확합니다. ch-이 요소 내에서 자체 중요성 문자열 자체를 포함하는 파일. 시작 및 길이-라인 및 길이의 시작 지점을 나타내는 서비스 수.

@Override 
public void characters (char [] ch, int start, int length) throws SAXException {
   if (thisElement.equals ("id")) {
      doc.setId (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("fam")) {
      doc.setFam (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("name")) {
      doc.setName (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("otc")) {
      doc.setOtc (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateb")) {
      doc.setDateb (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datep")) {
      doc.setDatep (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datev")) {
      doc.setDatev (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datebegin")) {
      doc.setDatebegin (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateend")) {
      doc.setDateend (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("vdolid")) {
      doc.setVdolid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("specid")) {
      doc.setSpecid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("klavid")) {
      doc.setKlavid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("stav")) {
      doc.setStav (new Float (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("progid")) {
      doc.setProgid (new Integer (new String (ch, start, length))); 
   } 
} 

아. 넵. 나는 거의 잊었다. 그 목적은 naparsennye 데이터를 접는 것이므로 의사의 유형을 말합니다. 이 클래스는 정의되어 있으며 필요한 모든 setter-getter가 있습니다.

다음 명백한 요소가 끝나고 다음 요소가 이어집니다. endElement () 종료를 담당합니다. 항목이 종료되었으며이 시점에서 무엇이든 할 수 있음을 알려줍니다. 계속할 것입니다. 정화 요소.

@Override 
public void endElement (String namespaceURI, String localName, String qName) throws SAXException {
   thisElement = ""; 
} 

전체 문서가 나오면 파일의 끝 부분에 도달합니다. 작업 endDocument (). 그 안에 메모리를 비우고, 진단 에스 유 인쇄 등을 할 수 있습니다. 우리의 경우에는 파싱이 끝나는 것에 대해 작성합니다.

@Override 
public void endDocument () {
   System.out.println ("Stop parse XML ..."); 
} 

그래서 우리는 xml 우리의 형식을 파싱하는 클래스를 얻었습니다. 다음은 전체 텍스트입니다.

import org.xml.sax.helpers.DefaultHandler; 
import org.xml.sax. *; 
 
public class SAXPars extends DefaultHandler {
 
Doctors doc = new Doctors (); 
String thisElement = ""; 
 
public Doctors getResult () {
   return doc; 
} 
 
@Override 
public void startDocument () throws SAXException {
   System.out.println ("Start parse XML ..."); 
} 
 
@Override 
public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
   thisElement = qName; 
} 
 
@Override 
public void endElement (String namespaceURI, String localName, String qName) throws SAXException {
   thisElement = ""; 
} 
 
@Override 
public void characters (char [] ch, int start, int length) throws SAXException {
   if (thisElement.equals ("id")) {
      doc.setId (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("fam")) {
      doc.setFam (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("name")) {
      doc.setName (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("otc")) {
      doc.setOtc (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateb")) {
      doc.setDateb (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datep")) {
      doc.setDatep (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datev")) {
      doc.setDatev (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("datebegin")) {
      doc.setDatebegin (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("dateend")) {
      doc.setDateend (new String (ch, start, length)); 
   } 
   if (thisElement.equals ("vdolid")) {
      doc.setVdolid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("specid")) {
      doc.setSpecid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("klavid")) {
      doc.setKlavid (new Integer (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("stav")) {
      doc.setStav (new Float (new String (ch, start, length))); 
   } 
   if (thisElement.equals ("progid")) {
      doc.setProgid (new Integer (new String (ch, start, length))); 
   } 
} 
 
@Override 
public void endDocument () {
   System.out.println ("Stop parse XML ..."); 
} 
} 

이 주제가 SAX 파서의 본질을 쉽게 표현하는 데 도움이 되었기를 바랍니다.

첫 번째 기사를 엄격하게 판단하지 마십시오. :) 적어도 유용한 사람 이었으면합니다.

UPD :이 파서를 실행하려면 다음 코드를 사용할 수 있습니다.

SAXParserFactory factory = SAXParserFactory.newInstance (); 
SAXParser parser = factory.newSAXParser (); 
SAXPars saxp = new SAXPars (); 
 
parser.parse (new File ("..."), saxp); 


public class MainActivity extends AppCompatActivity {
   ListView lvPcsPost;
    ArrayList<String> name;
    ArrayList<String> price;
    ArrayList<String> Description;
    LayoutInflater layoutInflater;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lvPcsPost = (ListView) findViewById(R.id.lvPcsPost);
        name = new ArrayList<>();
        price = new ArrayList<>();
        Description = new ArrayList<>();
        new PostAsync().execute();
    }

    class PostAsync extends AsyncTask<Void, Void, Void> {
        ProgressDialog pd;
        XMLHelper helper;


        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(MainActivity.this, "", "Loading...", true, false);
        }

        @Override
        protected Void doInBackground(Void... arg0) {
            helper = new XMLHelper();
            helper.get();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            PostBaseAdapter postBaseAdapter = new PostBaseAdapter();
            lvPcsPost.setAdapter(postBaseAdapter);
            pd.dismiss();
        }

    }

    public class XMLHelper extends DefaultHandler {

        private String URL_MAIN = "http://uat.winitsoftware.com/ThemeManager/Data/Products/Products.xml";
        String TAG = "XMLHelper";

        Boolean currTag = false;
        String currTagVal = "";

        public void get() {
            try {
                SAXParserFactory factory = SAXParserFactory.newInstance();
                SAXParser mSaxParser = factory.newSAXParser();
                XMLReader mXmlReader = mSaxParser.getXMLReader();
                mXmlReader.setContentHandler(this);
                InputStream mInputStream = new URL(URL_MAIN).openStream();
                mXmlReader.parse(new InputSource(mInputStream));
            } catch (Exception e) {
                Log.e(TAG, "Exception: " + e.getMessage());
            }
        }

        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            if (currTag) {
                currTagVal = currTagVal + new String(ch, start, length);
                currTag = false;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            currTag = false;

            if (localName.equalsIgnoreCase("Name"))
                name.add(currTagVal);

            else if (localName.equalsIgnoreCase("Description"))
             Description.add(currTagVal);

            else if (localName.equalsIgnoreCase("Price"))
              price.add(currTagVal);

        }
        @Override
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {
            Log.i(TAG, "TAG: " + localName);

            currTag = true;
            currTagVal = "";
            if (localName.equals("Products"));
        }
    }

    public class PostBaseAdapter extends BaseAdapter {

        public PostBaseAdapter() {

        }

        @Override
        public int getCount() {
            return name.size();
        }

        @Override
        public Object getItem(int position) {
            return name.get(position);
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            layoutInflater = LayoutInflater.from(getApplicationContext());

            convertView = layoutInflater.inflate(R.layout.list_item_post, parent, false);
            TextView  txtPrice = (TextView) convertView.findViewById(R.id.txtPrice);
            TextView  txtDescription = (TextView) convertView.findViewById(R.id.txtDescription);
            TextView   txtName = (TextView) convertView.findViewById(R.id.txtName);
            ImageView   image = (ImageView) convertView.findViewById(R.id.Image);
            ImageView  bigImage = (ImageView) convertView.findViewById(R.id.BigImage);

                txtPrice.setText("Price : "+price.get(position));
                 txtDescription.setText("Description : "+Description.get(position));
                txtName.setText("Name : "+name.get(position));

            return convertView;
        }
    }
}

참고 URL : https://stackoverflow.com/questions/4827344/how-to-parse-xml-using-the-sax-parser

반응형