Przeszukiwanie XML

0

Witam.
Jestem w trakcie pisania aplikacji, która będzie pobierała(wraz z uruchomieniem) aktualne kursy walut z pliku .xml, znajdującego się na stronie NBP, a następnie będzie ich używała do obliczeń wymiany walut. Jak do tej pory udaje mi się czytać całą zawartość pliku, pobierając strumień z URL. Jednak nie potrafię, zrealizować tego, aby w tym xml'u wyszukało mi wartość (kurs) dla danej-potrzebnej waluty. Przeczytałem już trochę w sieci i w żaden sposób, nie wiem jak tego dokonać [glowa] . Nigdy tego wcześniej nie robiłem, dlatego liczę na wyrozumiałość :). A o to mój dotychczasowy kod:

Najpierw fragment pliku xml, który należy przeszukać:

...
<pozycja>
  <nazwa_waluty>dolar amerykański</nazwa_waluty> 
  <przelicznik>1</przelicznik> 
  <kod_waluty>USD</kod_waluty> 
  <kurs_sredni>2,0875</kurs_sredni> 
  </pozycja>
- <pozycja>
  <nazwa_waluty>dolar australijski</nazwa_waluty> 
  <przelicznik>1</przelicznik> 
  <kod_waluty>AUD</kod_waluty> 
  <kurs_sredni>1,9100</kurs_sredni> 
  </pozycja>
...

A o to klasa z Handlerem:

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class Test_Class1 extends DefaultHandler {
	
	public Test_Class1(){
		super();
	}
	
	public void startDocument (){
		System.out.println("Start document");
	}

	public void endDocument (){
		System.out.println("End document");
	}

	public void startElement (String uri, String name, String qName, Attributes atts){
	if ("".equals (uri))
	    System.out.println("Start element: " + qName);
	else
	    System.out.println("Start element: {" + uri + "}" + name);
  }


  public void endElement (String uri, String name, String qName){
	if ("".equals (uri))
	    System.out.println("End element: " + qName);
	else
	    System.out.println("End element:   {" + uri + "}" + name);
 }


  public void characters (char ch[], int start, int length){
	System.out.print("Characters:    \"");
	for (int i = start; i < start + length; i++) {
	    switch (ch[i]) {
	    case '\\':
		System.out.print("\\\\");
		break;
	    case '"':
		System.out.print("\\\"");
		break;
	    case '\n':
		System.out.print("\\n");
		break;
	    case '\r':
		System.out.print("\\r");
		break;
	    case '\t':
		System.out.print("\\t");
		break;
	    default:
		System.out.print(ch[i]);
		break;
	    }
	}
	System.out.print("\"\n");
  }




	public static void main(String[] args) throws Exception{
		
		XMLReader myReader = XMLReaderFactory.createXMLReader();
		Test_Class1 handler = new Test_Class1();
		myReader.setContentHandler(handler);
		myReader.setErrorHandler(handler);
		myReader.parse(new InputSource(ParsingXML.inputStream()));
	
}
}

I klasa łączące się z URL:


import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.xml.sax.helpers.DefaultHandler;

public class ParsingXML extends DefaultHandler{

	
public static InputStream inputStream(){
		
		try{
			URL nbp = new URL("http://www.nbp.pl/Kursy/xml/a152z080805.xml");
			URLConnection urlConn = nbp.openConnection();

			return urlConn.getInputStream();

		}catch(MalformedURLException e){
			e.printStackTrace();
			
		}catch(IOException e){
			e.printStackTrace();
		}
		return null;
	}
}


0

Ja kiedyś też próbowałem coś działać parserem generującym zdarzenia, ale to jest... teraz bym tego tak nie zrobił. Polecam użyć biblioteki w3c DOM albo JDOM. Jest jeszcze Jaxen, którym bardzo łatwo operować używając xpath. Można go użyć z jedną z wymienionych bibliotek, ale z tego co widziałem, to również samodzielnie da się to osiągnąć, popatrz w przykłady.

0

Czyli zmienić koncepcję? A wydawała mi się najprostsza i najabrdziej popularna? No, ale jak pisałem, nigdy nie miałem z tym do czynienia, więc pewnie dlatego. A możesz mi polecić jakiś dobry tutorial to taj biblioteki DOM?
ps. a a tym przykładzie, rzeczywiście, jest jeszcze wiele do zrobienia, żeby działał tak jak chce?

0

Przy użyciu w3c DOM można zrobić to tak:

package org.ovh.chodnik.domtest;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Main {
	
	public static void main(String[] args) {
		DomCreator domCreator=new DomCreator();
		domCreator.printValue("USD");

	}

}
class DomCreator {
	Document doc;

	public DomCreator() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		try {
			URL nbp = new URL("http://www.nbp.pl/Kursy/xml/a152z080805.xml");
			URLConnection urlConn = nbp.openConnection();
			builder = factory.newDocumentBuilder();
			doc = builder.parse(urlConn.getInputStream());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}
	}

	void printValue(String name) {
		if (doc != null) {
			NodeList nodeList = doc.getElementsByTagName("pozycja");
			for (int i = 0; i < nodeList.getLength(); i++) {
				Element pos = (Element) nodeList.item(i);
				NodeList childNodes = pos.getElementsByTagName("*");
				String vName = "";
				String vValue = "";
				for (int j = 0; j < childNodes.getLength(); j++) {
					Element posElem = (Element) childNodes.item(j);
					if (posElem.getNodeName().equals("kod_waluty")) {
						vName = posElem.getTextContent();
					} else if (posElem.getNodeName().equals("kurs_sredni")) {
						vValue = posElem.getTextContent();
					}
				}
				if (vName.equals(name)) {
					System.out.println("Kurs waluty " + vName + " wynosi "
							+ vValue);
					return;
				}
			}
		}
	}
}

Robiłem na Java 1.6 bo nie od zawsze był DOM w JRE. W wyniku dostałem:
Kurs waluty USD wynosi 2,0777

Tutorial dotyczący xml DOM można znaleźć na www.w3schools.com
Jest to ogólny tutek, bo interfejs z założenia jest taki sam w różnych językach (Java, JavaScript, PHP, pewnie jeszcze inne).

Kod z wykorzystaniem XPath mógłby pewnie być jeszcze prostszy.

0

Przy pomocy SAX zrobiłem to tak:
Klasa opisująca kurs

public class Kurs
{
    private String kod;
    private String kurs;
    //------------------------
    public Kurs(String kod,String kurs)
    {
        this.kod=kod;
        this.kurs=kurs;
    }
    //------------------------
    public String getKod()
    {
        return kod;
    }
    //------------------------
    public String getKurs()
    {
        return kurs;
    }
}

klasa parsująca

import java.io.*;
import java.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class Parser
{
    private File file=null;
    protected Vector<Kurs> items=new Vector<Kurs>();
    //------------------------
    public Parser(File file)
    {
        this.file=file;
    }
    //------------------------
    public void list() throws SAXException,IOException
    {
        XMLReader parser=XMLReaderFactory.createXMLReader();
        Handler h=new Handler(this);
        parser.setContentHandler(h);
        parser.parse("file:"+file.getAbsolutePath());
    }
    //------------------------
    public Vector<Kurs> getItems()
    {
        return items;
    }
}
//------------------------------------------------
class Handler extends DefaultHandler
{
    private String kod=null;
    private String kurs=null;
    private final int NOTHING=0;
    private final int KOD=1;
    private final int KURS=2;
    private int status=NOTHING;
    private Parser parent=null;
    //------------------------
    Handler(Parser parent)
    {
        this.parent=parent;
    }
    //------------------------
    public void characters(char[] ch,int start,int end) throws SAXException
    {
        String str=new String(ch,start,end);
        switch(status)
        {
            case KOD:
                kod=str;
                break;
            case KURS:
                kurs=str;
                break;
        }
        status=NOTHING;
    }
    //------------------------
    public void endDocument() throws SAXException
    {
    }
    //------------------------
    public void endElement(String uri,String localName,String qName) throws SAXException
    {
        if (qName.equalsIgnoreCase("Pozycja"))
        {
            Kurs k=new Kurs(kod,kurs);
            parent.items.add(k);
            kod=null;
            kurs=null;
        }
    }
    //------------------------
    public void startDocument() throws SAXException
    {
        super.startDocument();
    }
    //------------------------
    public void startElement(String uri,String localName,String qName,Attributes atr) throws SAXException
    {
        if (qName.equalsIgnoreCase("Kod_waluty"))
        {
            status=KOD;
        }
        if (qName.equalsIgnoreCase("Kurs_sredni"))
        {
            status=KURS;
        }
    }
}
0

Dzięki za pomoc :). Powalcze z tym dzisiaj, mam nadzieję, że obejdzie się bez problemów.

Pozdrawiam

0

Problem się jednak pojawił:(. Na stronie NBP, na której znajduje się plik XML, z którego pobieram dane, jak wiadomo jest aktualiowany codzień. Jednakże modyfikowana jest nie tylko jego zawartość, ale jak się dziś przekonałem, również nazwa :/ . W jaki sposób można to rozwiązać? Jak uzyskać dokładny adres URL zasobu, przy każdorazowym uruchomieniu aplikacji?
I drugie pytanie...zacząłem najpierw pisać przykła @chodnika i nie do końca jasne, jest dla mnie to co dzieje się po pierwszej pętli for. Jeśli byłby, ktoś w stanie mi to rozjaśnić, to byłbym zadowolony. Chodzi mi dokładnie o to:

                            Element pos = (Element) nodeList.item(i);
                            NodeList childNodes = pos.getElementsByTagName("*");

0

W materiałach na w3schools jest zapewne opisane o co chodzi, ale w skrócie wygląda to tak:
metoda getElementsByTagName pobiera wszystkie elementy o nazwie zgodnej z podaną, jeżeli nazwą jest gwiazdka (""), to pobierane są wszystkie elementy. Ponieważ metoda ta jest wywoływana na obiekcie węzła, to zawęża to zakres przeszukiwania do jego elementów podrzędnych. Metoda ta zwraca listę pasujących elementów (NodeList). Kolejne elementy z listy są dostępne metodą item(index). Zmienna nodeList zawiera elementy o nazwie "pozycja". W pętli zmiennej pos typu Element przypisywane są kolejne elementy z listy reprezentujące tag "pozycja". Każdy z tych elementów ma elementy dzieci: nazwa_waluty, kod_waluty, przelicznik, kurs_sredni. Elementy te są pobierane jako lista metodą: pos.getElementsByTagName("");
Wywołanie na obiekcie pos zawęża zakres wyszukiwania. Ostatecznie w liście childNodes są cztery węzły dzieci.
Dalej sprawdzane są nazwy węzłów i ich zawartość.

Co do nazwy pliku: nie można wylistować katalogu nadrzędnego, więc właściwie nie ma możliwości pobrania nazw plików. (Może jest? ja myślę, że się nie da). Ale za to można przewidzieć tą nazwę, bo składa się ona z daty i kolejnego numeru tabeli, trzeba tylko pominąć dni wolne, kiedy nie ma kursów.

0

Na tej stronie NBP jest też link do html, którego nazwa się nie zmienia:). Więc będę musiał pewnie czytać z tego html, a nie z xml. Moglibyście mi podać jakiś przykład jak powinno wyglądąc parsowanie html, najlepiej analogicznie do przykładu @chodnika, który jest dla mnie najbardziej zrozumiały, aczkolwiek, za każdy przykład kodu realizujący wyciąganie danbych ( w tym wypadku kursów walut) będę wdzięczny. Jeśli tyczy się parsowania html, to może dobrym pomysłem byłoby przeszukać html, pod względem wyszukania linku do zmieniającego się pliku xml i wtedy przeparsować xml. Chociaż i tak chyba najpierw trzeba przeszukać html , w poszukiwaniu linku do xml? Pozdrawiam

0

Jeśli tyczy się parsowania html, to może dobrym pomysłem byłoby przeszukać html, pod względem wyszukania linku do zmieniającego się pliku xml i wtedy przeparsować xml.

To jest do zrealizowania. W sieci można znaleźć mnóstwo parserów do html. Można też użyć wbudowanej klasy, opis użycia jest tutaj: http://java.sun.com/products/jfc/tsc/articles/bookmarks/ Wtedy można wyciągnąć ze strony link do pliku xml. Da się też wyciągnąć te dane bezpośrednio z pliku html.

0

Przeszukałem dokument html w poszukiwaniu linku to pliku xml, a następnie przeszukuje sobie ten link (plik xml) w poszukiwaniu, interesującej mnie wartości waluty. Pojawił się inny problem. Jeśli chodzi o wyszukanie linku, to sprawa jest załatwiona, jednak chciałbym teraz wyszukiwać tekst w pliku HTML, a zeby pobierac jeszcze date, z ktorgo dnia są podane kursy walut. Czytałem o http://htmlparser.sourceforge.net/, dzięki tej bibliotece, wyciągnąłem link to pliku xml ze strony http://www.nbp.pl/Kursy/Kursya.html. Natomiast nie potrafię sobie poradzić z wyciągnięciem tekstu (dokładnie daty kursów w tym pliku html) :(. Czy dysponujecie może jakimiś przykładami, jak to powinno wyglądać, czy ktoś używał z Was już tej biblioteki, ponieważ z zamieszczonych tam informacji, nie jestem w stanie tego napisać. Pozdrawiam


No właśnie, dopiero poźniej zauważyłem :). No i już zrobione. Dzięki. Pozdr

0

Data tabeli jest w nazwie pliku xml, to chba najprostszy sposób na wyciągnięcie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1