Čitanje XML-a s pomoću .NET klasa

Nov 14, 2014 at 12:16 PM
Edited Nov 14, 2014 at 8:09 PM
Pozdrav,

S snimanjem XML-a RacunZahtjev i RacunOdgovor pojavilo se "čudo posla" koji se odnosi na backupe, čitanje, zbrajanje, izvješća i slično a u vezi s XML-om.

Kako to nisam prije radio pogldao sam u Raverus source kako on radi funkciju "Dohvati Jir" i prema tomu uspješno složio funkciju koja mi vadi string vrijednost uz zadani element.
No, problem je u tomu što kad vadim "tns:Racun/tns:Pdv/tns:Porez/tns:Stopa" uvijek dobijem podatke samo za prvu stopu poreza a čest je slučaj da ih ima više.
Isto tako u budućnosti je moguće da bude više stopa poreza na potrošnju pa bih ja to rado predvidio.

Evo "mog" koda:
XmlDocument doc = new XmlDocument();
doc.Load(folder + "\\" + XmlFile);
XmlNamespaceManager nsmgr = default(XmlNamespaceManager);
nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("tns", "http://www.apis-it.hr/fin/2012/types/f73");
XmlElement root = doc.DocumentElement;

string idporuke = "";
string datvrijeme = "";
string[] porezstopa = null;
string[] osnovica = null;
string[] iznos = null;
int xerror = 0;

XmlNode node = root.SelectSingleNode("tns:Zaglavlje/tns:IdPoruke", nsmgr);
if (node != null) idporuke = node.InnerText; else   xerror = +1;

node = root.SelectSingleNode("tns:Racun/tns:DatVrijeme", nsmgr);
if (node != null) datvrijeme = node.InnerText; else xerror = +2;

node = root.SelectSingleNode("tns:Racun/tns:Pdv/tns:Porez/tns:Stopa", nsmgr);
if (node != null)  porezstopa(0) = node.InnerText; else xerror = +4;

node = root.SelectSingleNode("tns:Racun/tns:Pdv/tns:Porez/tns:Stopa", nsmgr);
if (node != null)  porezstopa(1) = node.InnerText; else xerror = +8;
Pitanje je kako izvaditi drugu stopu poreza i pripadajuće podatke?
Molim ako netko može i želi pomoći jer prvi put radim s XML podacima i već sam razmišljao da napravim parser .NET string manipulacijama ali mislim da bi to bilo sporije.

Podaci koje koristim za test su ovdje:
<tns:RacunZahtjev Id="signXmlId">
<tns:Zaglavlje>
 <tns:IdPoruke>25978667-b873-49a6-a082-4f72c2fed7ee</tns:IdPoruke>
 <tns:DatumVrijeme>14.11.2014T13:01:42</tns:DatumVrijeme>
 </tns:Zaglavlje>
<tns:Racun>
 <tns:Oib>xxxxxxxxxxx</tns:Oib>
 <tns:USustPdv>true</tns:USustPdv>
 <tns:DatVrijeme>14.11.2014T13:01:25</tns:DatVrijeme>
 <tns:OznSlijed>P</tns:OznSlijed>
<tns:BrRac>
 <tns:BrOznRac>12629</tns:BrOznRac>
 <tns:OznPosPr>01</tns:OznPosPr>
 <tns:OznNapUr>01</tns:OznNapUr>
 </tns:BrRac>
<tns:Pdv>
<tns:Porez>
 <tns:Stopa>13.00</tns:Stopa>
 <tns:Osnovica>0.86</tns:Osnovica>
 <tns:Iznos>0.11</tns:Iznos>
 </tns:Porez>
<tns:Porez>
 <tns:Stopa>25.00</tns:Stopa>
 <tns:Osnovica>4.69</tns:Osnovica>
 <tns:Iznos>1.17</tns:Iznos>
 </tns:Porez>
 </tns:Pdv>
<tns:Pnp>
<tns:Porez>
 <tns:Stopa>3.00</tns:Stopa>
 <tns:Osnovica>5.55</tns:Osnovica>
 <tns:Iznos>0.17</tns:Iznos>
 </tns:Porez>
 </tns:Pnp>
 <tns:IznosUkupno>7.00</tns:IznosUkupno>
 <tns:NacinPlac>G</tns:NacinPlac>
 <tns:OibOper>xxxxxxxxxxx</tns:OibOper>
 <tns:ZastKod>d49719907b45982e9464ac734f2ea050</tns:ZastKod>
 <tns:NakDost>false</tns:NakDost>
 </tns:Racun>
 </tns:RacunZahtjev>
Nov 14, 2014 at 8:09 PM
Edited Nov 14, 2014 at 8:12 PM
Eto, uradiše ja to i sam.
Ovako se čitaju višestruki porezi:
dynamic porezi = root.SelectNodes("tns:Racun/tns:Pdv/tns:Porez", nsmgr);
int temp = 0;
foreach (XmlNode pdv in porezi) {
    if (pdv("tns:Stopa") != null) {
        Array.Resize(ref porezstopa, temp);
        porezstopa(temp) = pdv("tns:Stopa").InnerText;
    }
    if (pdv("tns:Osnovica") != null) {
        Array.Resize(ref osnovica, temp);
        osnovica(temp) = pdv("tns:Osnovica").InnerText;
    }
    if (pdv("tns:Iznos") != null) {
        Array.Resize(ref iznos, temp);
        iznos(temp) = pdv("tns:Iznos").InnerText;
    }
    temp += 1;
}
Nov 17, 2014 at 11:56 PM
Mala digresija glede programiranja...
Ocigledno je da provjeravas postojanje 3 podatka (stopa, osnovica i iznos) te ih onda trpas u neke nizove. Sto ako nema neke osnovice (teoretski)? Niz postane desinkroniziran, tj. indexi niza nakon nedostajuce osnovice vise ne pokazuju pripadajuce im podatke. Stoga, po meni, treba testirati ili sva tri odjednom ili bilo koji (jedan, ako smo 100% sigurni da ce onda i druga 2 podatka biti tu). Ja bih to ovako:
if stopa != null .and. osnovica != null .and. iznos != null  { 
   dodaj sve te podatke u niz i index +1
}
Ako sva tri nisu tu, onda ga uopce ne uzimaj u obzir ili napravi neki error exception.
Nov 18, 2014 at 10:44 PM
Već sam to bio uočio i ispravio ali na malo drugačiji način, dimenzioniranjem svih polja prije ikakve provjere, odmah iza "foreach" tako da se stvar ne može desinkronizirati.
Mislim da na tom mjestu uopće ne bi trebalo raditi "servis greške" pa čak ni null check jer je bolje da program "pukne" nego da sakrije grešku i pošalje što ima.
Ovako kako je sada ako podatka (teoretski) nema on će reći da je nula, a nije - nema ga i to nije ispravan fajl a proći će kao ispravan.
Servis greške i inače može biti opasna stvar.
Ipak, u praksi imam na svakom koraku množenje greške tako da na kraju uzimam podatke samo ako je greška 0. Zašto tako? Zato jer u fajlu može biti više grešaka od jedne pa se dekodiranjem broja greške može dobiti info o svim pogrešnim mjestima u fajlu a ne samo jednom.
Na taj način program uspješno preskače nodove Pnp kojega nema na svim računima...

Tvoja ideja s trostrukom provjerom odjednom izgleda da bi bila bolje rješenje za prikazani dio.