14. december 2019 - 13:43Der er
17 kommentarer og 2 løsninger
StAX pattern, best practice?
Jeg skal læse en stor XML-fil, og har derfor besluttet mig for at benytte StAX. Jeg har valgt at benytte cursor-metoden (og altså ikke iterator-metoden).
Alle de tuturials, jeg har set, viser kun, hvordan man bruger API'et. De benytter derfor naturligvis en ukompliceret XML-fil. fx en klasse med mange elever med navn, adresse, osv. Her er det typisk en while-lykke, med følgende pattern:
public void parseXMLDocument(String XMLfilename) { while(xmlStreamReader.hasNext()){ int event=xmlStreamReader.next(); Switch (event) { case... case... case... } } }
Men, lad os nu forestille os et land med mange skoler med mange klasser med mange elever med navn, adresse, (og endnu mere kompliceret) Her kan jeg forestille mig to patterns:
1) Et pattern med switch-sætninger inde i switch-sætninger inde i switch-sætninger, osv MEN, med kun een while-lykke med ,xmlStreamReader.hasNext() som i koden ovenfor. Fordelen ved pattern 1 er, at det er en flad metode med een while-lykke, og kun een class. Ulemperne er, at der er nestede switch-sætninger, samt at metoden vil bive ufattelig lang (læs 1000+ linier).
2) Et pattern et antal metoder, hvor hver metode læser hvert sit level i XML-strukturen, dvs. en for documentet som sådan, en for landet, en for skolen, en for klassen, en for eleven. MEN, hver metode skal så have sin egen while-lykke med sin egen xmlStreamReader.hasNext(). Fordelen ved pattern 2 er, at hver level i XML-filen har sin egen metode, så metoderne bliver små. Ulemperne er, at der skal een metode for hver XML-element, samt at hver metode skal have sin egen while-sætning med sin eget hasNext()-kald. Det giver *mange* metoder, (og mange classes)
Her er så endelig mit spørgsmål: Hvad er best practice med komplicerede XML-filer? Pattern 1 eller 2, eller har jeg overset et hel tredie pattern? Umiddelbart hælder jeg til 2'eren, da den er mest struktureret, men jeg orker næsten ikke at skrive 50+ classes.
<alleskoler> <skole navn='Skole X'> <klasse navn='1'> <elev id='1'> <navn>A A</navn> <adresse>A Vej 1</adresse> </elev> <elev id='2'> <navn>B B</navn> <adresse>B Vej 2</adresse> </elev> </klasse> <klasse navn='2'> <elev id='3'> <navn>C C</navn> <adresse>C Vej 3</adresse> </elev> <elev id='4'> <navn>D D</navn> <adresse>D Vej 4</adresse> </elev> </klasse> </skole> <skole navn='Skole Y'> <klasse navn='8'> <elev id='5'> <navn>E E</navn> <adresse>E Vej 5</adresse> </elev> <elev id='6'> <navn>F F</navn> <adresse>F Vej 6</adresse> </elev> </klasse> <klasse navn='9'> <elev id='7'> <navn>G G</navn> <adresse>G Vej 7</adresse> </elev> <elev id='8'> <navn>H H</navn> <adresse>H Vej 8</adresse> </elev> </klasse> </skole> </alleskoler> skole = Skole X klasse = 1 elev navn = A A adresse = A Vej 1 elev navn = B B adresse = B Vej 2 klasse = 2 elev navn = C C adresse = C Vej 3 elev navn = D D adresse = D Vej 4 skole = Skole Y klasse = 8 elev navn = E E adresse = E Vej 5 elev navn = F F adresse = F Vej 6 klasse = 9 elev navn = G G adresse = G Vej 7 elev navn = H H adresse = H Vej 8 skole = Skole X klasse = 1 elev navn = A A adresse = A Vej 1 elev navn = B B adresse = B Vej 2 klasse = 2 elev navn = C C adresse = C Vej 3 elev navn = D D adresse = D Vej 4 skole = Skole Y klasse = 8 elev navn = E E adresse = E Vej 5 elev navn = F F adresse = F Vej 6 klasse = 9 elev navn = G G adresse = G Vej 7 elev navn = H H adresse = H Vej 8 skole = Skole X klasse = 1 elev navn = A A adresse = A Vej 1 elev navn = B B adresse = B Vej 2 klasse = 2 elev navn = C C adresse = C Vej 3 elev navn = D D adresse = D Vej 4 skole = Skole Y klasse = 8 elev navn = E E adresse = E Vej 5 elev navn = F F adresse = F Vej 6 klasse = 9 elev navn = G G adresse = G Vej 7 elev navn = H H adresse = H Vej 8
Men med tilpas komplekse strukturer må den kontekst baserede metode give mere overskuelig kode.
Men det er bare mig.
Jeg tror at de fleste kun bruger SAX/StAX til simpel meget stor XML mens man bruger XML binding (JAXB i Java) eller DOM med XPath til kompleks mindre XML.
Og at grunden til at der er lidt omkirng kompleks meget stor XML er at det sjældent er situationen.
Det XML-format, som jeg skal læse, har mange forskellige tags (50+) Volumenmæssigt varierer det en del. Den største XML-fil er pt. 95 MB. Men jeg skal kunne læse større filer end den.
Det første pattern, jeg beskrev, havde jeg tænkt som en tilstandsmaskine. Derfor de mange switches.
Jeg synes, at begge dine forslag er bedre end mine :-(. Jeg går helt klart ind for velstruktureret kode, men jeg er også doven! Jeg vil jo gerne have både i pose og i sæk. Det ser ud til, at dine forslag giver mig det.
Jeg prøvede også at lave et eksempel med state, men jeg stoppede da det ikke syntes at give meget værdi.
Det grundliggende problem synes at være at hvis man kender state, så ved man at der kun er få mulige input, men man skal alligevel hente og teste på næste input, og valideringen kan laves via DTD eller schema validering.
"400 MB kan sagtens være i memory idag." Ja, sådan burde det være, men jeg hoster min JVM hos Levonline i Stockholm, og der tilbyder de kun 64 MB i den lille version og 256 MB i deluxe-udgaven. De tilbyder iøvrigt også kun Java 1.7. Jeg skulle på et tidspunkt bruge Java 1.8-funktionalitet. Min work-around var at hente kildeteksten og oversætte den i v1.7, og så kørte det. Ja, nogle gange må man sno sig.
Jeg har lige et tillægsspørgsmål: Når jeg læser xml-filen ind fra en fil, går det ok, så længe jeg ikke har fx æøå. Prøv fx denne version: public static void contextBased(String filename) throws XMLStreamException, FileNotFoundException { XMLInputFactory xif = XMLInputFactory.newInstance(); InputStream input = new FileInputStream(new File(filename)); XMLStreamReader xsr = xif.createXMLStreamReader(input); if(moveStartElement(xsr, "alleskoler", null)) { processAlleSkoler(xsr); } xsr.close(); } public static void main(String[] args) throws XMLStreamException, FileNotFoundException { if (args[0]==null) { System.out.println("XML input fil mangler"); } else { contextBased(args[0]); } } , hvor skolen hedder "Blåbærgrød", så får jeg fejlen: "Invalid byte 3 of 3-byte UTF-8 sequence." Hvorfor det?!? Jeg læser jo filen ind som en binær fil.
Hvis jeg skulle gætte: Java antager at filen er i UTF-8 encoding enten fordi XML filen siger det eller fordi det er default, men filen er faktisk i en anden encoding formentligt ISO-8859-1.
UTF-8 labeled as UTF-8: OK ISO-8859-1 labeled as ISO-8859-1: OK UTF-8 labeled as ISO-8859-1: OK ISO-8859-1 labeled as UTF-8: Invalid byte 2 of 3-byte UTF-8 sequence.
Ja eller å = 11100101 Tak! Jeg bryder mig ikke om, når der er noget jeg ikke forstår, så nu kan jeg sove roligt :-)
Jeg har opdaget en besynderlighed ved StAX:
<item name="navn">Hans og Grete</item> her er CHARACTERS: "Hans og Grete" MEN: <item name="navn">Hans & Grete</item> her er CHARACTERS: "Hans", "&", "Grete"
Kan man undgå, at CHARACTERS bliver delt? Der er tegn, der er ulovlige, men så vidt jeg kan se af dokumentationen, er "Hans & Grete" fuldt lovlig, så jeg kan ikke se nogen grund til, at det bliver delt i tre.
Det er en del af API at implementationen kan splitte tekst i flere characater events.
Begrundelsen er memory. Der kunne være en 500 MB tekst. Og hele pointen i streaming XML er at undgå store objekter i memory.
Det er naturligvis ikke et problem i dit tilfælde. Men here tror jeg bare at de forsøger at undgå et midlertidigt String objekt.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.