wake-up-neo.com

Wie analysiert man XML mit dem SAX-Parser?

Ich verfolge dieses Tutorial .

Es funktioniert großartig, aber ich möchte, dass ein Array mit allen Strings anstelle eines einzelnen Strings mit dem letzten Element zurückgegeben wird.

Irgendwelche Ideen, wie das geht?

58
Johan

Sie möchten also einen XML-Parser erstellen, um einen RSS-Feed wie diesen zu analysieren.

<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>

Jetzt haben Sie zwei SAX-Implementierungen, mit denen Sie arbeiten können. Entweder verwenden Sie die org.xml.sax- oder die Android.sax-Implementierung. Ich werde die Vor- und Nachteile beider erläutern, nachdem ich ein Short-Hander-Beispiel veröffentlicht habe.

Android.sax-Implementierung

Beginnen wir mit der Implementierung von Android.sax.

Sie müssen zunächst die XML-Struktur mit den Objekten RootElement und Element definieren.

In jedem Fall würde ich mit POJOs (Plain Old Java Objects) arbeiten, die Ihre Daten enthalten würden. Hier wären die POJOs nötig.

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
}

Diese Klasse implementiert die Serializable-Schnittstelle, sodass Sie sie in eine Bundle setzen und damit etwas tun können.

Jetzt brauchen wir eine Klasse, um unsere Gegenstände zu halten. In diesem Fall werde ich einfach die ArrayList-Klasse erweitern.

Items.Java

public class Items extends ArrayList<Item> {

    public Items() {
        super();
    }

}

Das ist es für unseren Artikelbehälter. Wir brauchen jetzt eine Klasse, um die Daten jedes einzelnen Elements zu speichern.

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.

}

Beispiel:

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;
    }

}

Das war ein sehr schnelles Beispiel, wie Sie sehen können. Der Hauptvorteil der Verwendung der Android.sax SAX-Implementierung besteht darin, dass Sie die Struktur des XML definieren können, das Sie analysieren müssen, und den entsprechenden Elementen einfach einen Ereignislistener hinzufügen. Der Nachteil ist, dass der Code ziemlich wiederholt und aufgebläht wird.

org.xml.sax Implementierung

Die Implementierung des org.xml.sax SAX-Handlers ist etwas anders.

Hier geben Sie keine XML-Struktur an oder deklarieren sie, sondern überwachen nur Ereignisse. Die am häufigsten verwendeten sind folgende Ereignisse:

  • Dokument starten
  • Dokumentende
  • Elementstart
  • Element Ende
  • Zeichen zwischen Element Start und Element End

Eine Beispielhandlerimplementierung, die das obige Channel-Objekt verwendet, sieht folgendermaßen aus.

Beispiel

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.
    }

}

Um ehrlich zu sein, kann ich Ihnen keinen wirklichen Vorteil dieser Handler-Implementierung gegenüber dem Android.sax sagen. Ich kann Ihnen jedoch den Nachteil nennen, der jetzt offensichtlich sein sollte. Sehen Sie sich die else if -Anweisung in der startElement-Methode an. Aufgrund der Tatsache, dass wir die Tags <title>, link und description haben, müssen wir dort in der XML-Struktur nachverfolgen, in der wir uns gerade befinden. Das heißt, wenn wir auf ein <item>-Starttag stoßen, setzen wir das inItem-Flag auf true, um sicherzustellen, dass wir die korrekten Daten dem richtigen Objekt zuordnen. In der endElement-Methode setzen wir dieses Flag auf false, wenn ein </item>-Tag auftritt. Um zu signalisieren, dass wir mit diesem Artikel-Tag fertig sind.

In diesem Beispiel ist das ziemlich einfach zu bewerkstelligen, aber das Analysieren einer komplexeren Struktur mit sich wiederholenden Tags in verschiedenen Ebenen wird schwierig. Dort müssten Sie entweder Enums verwenden, um beispielsweise Ihren aktuellen Status festzulegen, und viele Switch/Case-Statemenets, um zu überprüfen, wo Sie sich befinden, oder eine elegantere Lösung wäre eine Art Tag-Tracker, der einen Tag-Stack verwendet.

179

Bei vielen Problemen müssen verschiedene Arten von XML-Dateien für unterschiedliche Zwecke verwendet werden. Ich werde nicht versuchen, die Unermesslichkeit zu erfassen und aus eigener Erfahrung zu sagen, was ich alles brauchte. 

Java vielleicht meine Lieblingsprogrammiersprache. Darüber hinaus wird diese Liebe durch die Tatsache gestärkt, dass Sie jedes Problem lösen können und sich kein Fahrrad einfallen lassen muss. 

Ich brauchte also eine Reihe von Client-Servern, auf denen eine Datenbank ausgeführt wurde, die es dem Client ermöglichte, remote Einträge auf dem Datenbankserver vorzunehmen. Es ist unnötig, Eingabedaten usw. zu überprüfen, aber es geht nicht darum. 

Als Arbeitsprinzip habe ich mich ohne zu zögern für die Übermittlung von Informationen in Form einer XML-Datei entschieden. Von den folgenden Arten: 

<? 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> 

Machen Sie es sich leichter, weiter zu lesen, es sei denn, es handelt sich um Informationen über Ärzteinstitutionen. Nachname, Vorname, eindeutige ID usw. Im Allgemeinen sind die Datenreihen. Diese Datei ist sicher auf die Serverseite gelangt und beginnt dann mit der Analyse der Datei. 

Von den beiden Optionen Parsing (SAX vs DOM) wählte ich SAX aus der Sicht, dass er besser arbeitet, und er war der erste, dem ich in die Hände fiel :) 

So. Wie Sie wissen, müssen Sie die von DefaultHandler benötigten Methoden überschreiben, um erfolgreich mit dem Parser arbeiten zu können. Verbinden Sie zunächst die erforderlichen Pakete. 

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

Jetzt können wir unseren Parser schreiben 

public class SAXPars extends DefaultHandler {
   ... 
} 

Beginnen wir mit der Methode startDocument (). Er reagiert, wie der Name schon sagt, auf einen Ereignisanfang des Dokuments. Hier können Sie verschiedene Aktionen aufhängen, z. B. Speicherzuweisung oder zum Zurücksetzen der Werte. Unser Beispiel ist jedoch ziemlich einfach. Markieren Sie daher einfach den Beginn der Arbeit einer entsprechenden Nachricht: 

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

Nächster. Der Parser durchläuft das Dokument und das Element seiner Struktur. Startet die Methode startElement (). Und tatsächlich sieht sein Aussehen folgendermaßen aus: startElement (String namespaceURI, String localName, String qName, Attribute atts). Hier ist namespaceURI - der Namespace, localName - der lokale Name des Elements, qName - eine Kombination aus lokalem Namen mit einem Namespace (durch einen Doppelpunkt getrennt) und atts - den Attributen dieses Elements. In diesem Fall alles ganz einfach. Es genügt, qName'om zu verwenden und es in eine Teilzeile dieses Elements zu werfen. So markieren wir, in welchem ​​Element wir uns gerade befinden. 

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

Als nächstes kommen wir zu seiner Bedeutung. Fügen Sie hier Methoden zeichen () ein. Er hat die Form: Zeichen (char [] ch, int start, int length). Nun, hier ist alles klar. ch - eine Datei, die die Zeichenfolge selbst enthält, die innerhalb dieses Elements selbst wichtig ist. Start und Länge - die Nummer des Dienstes, die den Startpunkt in der Zeile und die Länge angibt. 

@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))); 
   } 
} 

Ah ja. Ich habe es fast vergessen. Als das Ziel davon sein soll, naparsennye Daten zu falten, spricht die Art der Ärzte an. Diese Klasse ist definiert und enthält alle erforderlichen Setzer. 

Das nächste offensichtliche Element endet und es folgt das nächste. Verantwortlich für das Beenden von endElement (). Es signalisiert uns, dass der Artikel zu Ende ist und Sie zu diesem Zeitpunkt alles tun können. Wird fortgesetzt. Element reinigen. 

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

Kommen also das gesamte Dokument, so kommen wir zum Ende der Datei. Arbeit endDocument (). Darin können wir Speicher freigeben, einige diagnostische Druckvorgänge durchführen usw. In unserem Fall schreiben Sie einfach, was das Parsing endet. 

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

Also haben wir eine Klasse bekommen, um das Format im XML-Format zu analysieren. Hier ist der vollständige Text: 

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 ..."); 
} 
} 

Ich hoffe, dass das Thema dazu beigetragen hat, die Essenz des SAX-Parsers leicht zu präsentieren. 

Urteile nicht strikt über den ersten Artikel :) Ich hoffe, es war zumindest jemand nützlich. 

UPD: Um diesen Parser auszuführen, können Sie diesen Code verwenden: 

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

2
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;
        }
    }
}
0
vimal singh