Προγραμματισμός

Επεξεργασία εγγράφων XML σε Java χρησιμοποιώντας XPath και XSLT

Το Extensible Markup Language (XML) είναι σίγουρα μια από τις πιο καυτές τεχνολογίες αυτή τη στιγμή. Ενώ η έννοια των γλωσσών σήμανσης δεν είναι νέα, το XML φαίνεται ιδιαίτερα ελκυστικό για προγραμματιστές Java και Internet. Το Java API για XML Parsing (JAXP; βλέπε πόρους), που έχει οριστεί πρόσφατα μέσω της διαδικασίας Java Community, υπόσχεται να παρέχει μια κοινή διεπαφή για την πρόσβαση σε έγγραφα XML. Το W3C έχει ορίσει το λεγόμενο μοντέλο αντικειμένου εγγράφου (DOM), το οποίο παρέχει μια τυπική διεπαφή για εργασία με ένα έγγραφο XML σε μια ιεραρχία δέντρων, ενώ το Simple API για XML (SAX) επιτρέπει σε ένα πρόγραμμα να αναλύσει ένα έγγραφο XML διαδοχικά, με βάση σε ένα μοντέλο χειρισμού συμβάντων. Και τα δύο αυτά πρότυπα (το SAX είναι ένα de facto πρότυπο) συμπληρώνουν το JAXP. Μαζί, αυτά τα τρία API παρέχουν επαρκή υποστήριξη για την αντιμετώπιση εγγράφων XML στην Java και πολλά βιβλία στην αγορά περιγράφουν τη χρήση τους.

Αυτό το άρθρο εισάγει έναν τρόπο χειρισμού εγγράφων XML που υπερβαίνει τα τυπικά API Java για χειρισμό XML. Θα δούμε ότι σε πολλές περιπτώσεις οι XPath και XSLT παρέχουν απλούστερους, πιο κομψούς τρόπους επίλυσης προβλημάτων εφαρμογής. Σε μερικά απλά δείγματα, θα συγκρίνουμε μια καθαρή λύση Java / XML με μια λύση που χρησιμοποιεί XPath και / ή XSLT.

Τόσο το XSLT όσο και το XPath αποτελούν μέρος της προδιαγραφής Extensible Stylesheet Language (XSL) (βλ. Πόροι). Το XSL αποτελείται από τρία μέρη: την ίδια την προδιαγραφή γλώσσας XSL, το XSL Transformations (XSLT) και το XML Path Language (XPath). Το XSL είναι μια γλώσσα για τη μετατροπή εγγράφων XML. Περιλαμβάνει έναν ορισμό - Μορφοποίηση αντικειμένων - του τρόπου μορφοποίησης εγγράφων XML για παρουσίαση. Το XSLT καθορίζει ένα λεξιλόγιο για τη μετατροπή ενός εγγράφου XML σε άλλο. Μπορείτε να θεωρήσετε το XSLT ως XSL μείον τη μορφοποίηση αντικειμένων. Η γλώσσα XPath απευθύνεται σε συγκεκριμένα μέρη εγγράφων XML και προορίζεται να χρησιμοποιηθεί μέσα από ένα φύλλο στυλ XSLT.

Για τους σκοπούς αυτού του άρθρου, θεωρείται ότι είστε εξοικειωμένοι με τα βασικά στοιχεία των XML και XSLT, καθώς και των API DOM. (Για πληροφορίες και σεμινάρια σχετικά με αυτά τα θέματα, ανατρέξτε στην ενότητα Πόροι.)

Σημείωση: Τα δείγματα κώδικα αυτού του άρθρου συγκεντρώθηκαν και δοκιμάστηκαν με το πρόγραμμα ανάλυσης Apache Xerces XML και τον επεξεργαστή Apache Xalan XSL (βλ. Πόροι).

Το πρόβλημα

Πολλά άρθρα και έγγραφα που ασχολούνται με την XML δηλώνουν ότι είναι το τέλειο όχημα για την επίτευξη καλής πρακτικής σχεδιασμού στον προγραμματισμό Ιστού: το πρότυπο Model-View-Controller (MVC) ή, με απλούστερους όρους, ο διαχωρισμός των δεδομένων εφαρμογής από τα δεδομένα παρουσίασης . Εάν τα δεδομένα της εφαρμογής είναι μορφοποιημένα σε XML, μπορούν εύκολα να δεσμευτούν - συνήθως σε servlet ή Java ServerPage - σε, για παράδειγμα, πρότυπα HTML χρησιμοποιώντας ένα φύλλο στυλ XSL.

Ωστόσο, η XML μπορεί να κάνει πολύ περισσότερα από το να βοηθήσει μόνο στο διαχωρισμό προβολής μοντέλου για το frontend μιας εφαρμογής. Επί του παρόντος παρατηρούμε ολοένα και πιο διαδεδομένη χρήση στοιχείων (για παράδειγμα, συστατικών που αναπτύχθηκαν χρησιμοποιώντας το πρότυπο EJB) που μπορούν να χρησιμοποιηθούν για τη συναρμολόγηση εφαρμογών, ενισχύοντας έτσι την παραγωγικότητα του προγραμματιστή. Η επαναχρησιμοποίηση των στοιχείων μπορεί να βελτιωθεί με τη μορφοποίηση των δεδομένων με τα οποία τα στοιχεία αντιμετωπίζουν με έναν τυπικό τρόπο. Πράγματι, μπορούμε να περιμένουμε να δούμε όλο και περισσότερα δημοσιευμένα στοιχεία που χρησιμοποιούν XML για να περιγράψουν τις διεπαφές τους.

Επειδή τα δεδομένα με μορφή XML είναι ουδέτερα από τη γλώσσα, καθίστανται χρήσιμα σε περιπτώσεις όπου ο πελάτης μιας δεδομένης υπηρεσίας εφαρμογής δεν είναι γνωστός ή όταν δεν πρέπει να έχει εξαρτήσεις από το διακομιστή. Για παράδειγμα, σε περιβάλλοντα B2B, ενδέχεται να μην είναι αποδεκτό για δύο μέρη να έχουν εξαρτήσεις από συγκεκριμένες διεπαφές αντικειμένων Java για την ανταλλαγή δεδομένων τους. Νέες τεχνολογίες όπως το Simple Object Access Protocol (SOAP) (βλ. Πόρους) καλύπτουν αυτές τις απαιτήσεις.

Όλες αυτές οι περιπτώσεις έχουν ένα κοινό σημείο: τα δεδομένα αποθηκεύονται σε έγγραφα XML και πρέπει να χειριστούν από μια εφαρμογή. Για παράδειγμα, μια εφαρμογή που χρησιμοποιεί διάφορα στοιχεία από διαφορετικούς προμηθευτές πιθανότατα θα πρέπει να αλλάξει τη δομή των δεδομένων (XML) ώστε να ταιριάζει με την ανάγκη της εφαρμογής ή να συμμορφώνεται με ένα δεδομένο πρότυπο.

Ο κώδικας που γράφτηκε χρησιμοποιώντας τα Java APIs που αναφέρθηκαν παραπάνω θα το έκανε σίγουρα. Επιπλέον, υπάρχουν όλο και περισσότερα διαθέσιμα εργαλεία με τα οποία μπορείτε να μετατρέψετε ένα έγγραφο XML σε JavaBean και αντίστροφα, γεγονός που διευκολύνει τον χειρισμό των δεδομένων από ένα πρόγραμμα Java. Ωστόσο, σε πολλές περιπτώσεις, η εφαρμογή, ή τουλάχιστον μέρος αυτής, απλώς επεξεργάζεται ένα ή περισσότερα έγγραφα XML ως είσοδο και τα μετατρέπει σε διαφορετική μορφή XML ως έξοδο. Η χρήση φύλλων στυλ σε αυτές τις περιπτώσεις είναι μια βιώσιμη εναλλακτική λύση, όπως θα δούμε αργότερα σε αυτό το άρθρο.

Χρησιμοποιήστε το XPath για να εντοπίσετε κόμβους σε ένα έγγραφο XML

Όπως αναφέρθηκε παραπάνω, η γλώσσα XPath χρησιμοποιείται για τον εντοπισμό ορισμένων τμημάτων ενός εγγράφου XML. Ως εκ τούτου, προορίζεται να χρησιμοποιηθεί από ένα φύλλο στυλ XSLT, αλλά τίποτα δεν μας εμποδίζει να το χρησιμοποιήσουμε στο πρόγραμμα Java για να αποφύγουμε τη μακρά επανάληψη μιας ιεραρχίας στοιχείων DOM. Πράγματι, μπορούμε να αφήσουμε τον επεξεργαστή XSLT / XPath να κάνει τη δουλειά για εμάς. Ας ρίξουμε μια ματιά στο πώς λειτουργεί αυτό.

Ας υποθέσουμε ότι έχουμε ένα σενάριο εφαρμογής στο οποίο ένα έγγραφο προέλευσης XML παρουσιάζεται στον χρήστη (πιθανώς μετά την επεξεργασία από ένα φύλλο στυλ). Ο χρήστης πραγματοποιεί ενημερώσεις στα δεδομένα και, για να εξοικονομήσει εύρος ζώνης δικτύου, στέλνει μόνο τις ενημερωμένες εγγραφές πίσω στην εφαρμογή. Η εφαρμογή αναζητά το τμήμα XML στο έγγραφο προέλευσης που πρέπει να ενημερωθεί και να το αντικαταστήσει με τα νέα δεδομένα.

Θα δημιουργήσουμε ένα μικρό δείγμα που θα σας βοηθήσει να κατανοήσετε τις διάφορες επιλογές. Για αυτό το παράδειγμα, υποθέτουμε ότι η εφαρμογή ασχολείται με εγγραφές διευθύνσεων σε ένα βιβλίο διευθύνσεων. Ενα δείγμα βιβλίο διευθύνσεων το έγγραφο μοιάζει με αυτό:

  John Smith 250 18th Ave SE Rochester MN 55902 Bill Morris 1234 Center Lane ΒΔ St. Paul MN 55123 

Η εφαρμογή (πιθανώς, αν και όχι απαραίτητα, servlet) διατηρεί μια παρουσία του βιβλίο διευθύνσεων στη μνήμη ως DOM Εγγραφο αντικείμενο. Όταν ο χρήστης αλλάζει μια διεύθυνση, η διεπαφή της εφαρμογής της στέλνει μόνο την ενημερωμένη στοιχείο.

ο Το στοιχείο χρησιμοποιείται για τον μοναδικό προσδιορισμό μιας διεύθυνσης. χρησιμεύει ως το βασικό κλειδί. Αυτό δεν θα είχε πολύ νόημα για μια πραγματική εφαρμογή, αλλά το κάνουμε εδώ για να κρατήσουμε τα πράγματα απλά.

Τώρα πρέπει να γράψουμε κάποιον κώδικα Java που θα μας βοηθήσει να αναγνωρίσουμε το στοιχείο στο δέντρο προέλευσης που πρέπει να αντικατασταθεί με το ενημερωμένο στοιχείο. ο εύρεση διεύθυνσης () Η παρακάτω μέθοδος δείχνει πώς μπορεί να επιτευχθεί. Λάβετε υπόψη ότι, για να διατηρήσουμε το δείγμα σύντομο, έχουμε αφήσει τον κατάλληλο χειρισμό σφαλμάτων.

δημόσιος κόμβος findAddress (όνομα συμβολοσειράς, πηγή εγγράφου) {Element root = source.getDocumentElement (); NodeList nl = root.getChildNodes (); // επαναλάβετε όλους τους κόμβους διευθύνσεων και βρείτε αυτόν που έχει τον σωστό παραλήπτη για (int i = 0; i

Ο παραπάνω κώδικας πιθανότατα να βελτιστοποιηθεί, αλλά είναι προφανές ότι η επανάληψη πάνω από το δέντρο DOM μπορεί να είναι κουραστική και επιρρεπής σε σφάλματα. Τώρα ας δούμε πώς μπορεί να εντοπιστεί ο κόμβος προορισμού χρησιμοποιώντας μια απλή δήλωση XPath. Η δήλωση θα μπορούσε να έχει την εξής μορφή:

// διεύθυνση [child :: addressee [text () = 'Jim Smith']] 

Τώρα μπορούμε να ξαναγράψουμε την προηγούμενη μέθοδο μας. Αυτή τη φορά, χρησιμοποιούμε τη δήλωση XPath για να βρούμε τον επιθυμητό κόμβο:

δημόσιος κόμβος findAddress (Όνομα συμβολοσειράς, πηγή εγγράφου) ρίχνει την εξαίρεση {// πρέπει να δημιουργήσει ξανά μερικά βοηθητικά αντικείμενα XPathProcessor xpathParser = νέο XPathProcessorImpl (xpathSupport); PrefixResolver prefixResolver = νέο PrefixResolverDefault (source.getDocumentElement ()); // δημιουργήστε το XPath και αρχικοποιήστε το XPath xp = νέο XPath (); String xpString = "// address [child :: addressee [text () = '" + name + "']]"; xpathParser.initXPath (xp, xpString, prefixResolver); // τώρα εκτελέστε τη δήλωση επιλογής XPath XObject list = xp.execute (xpathSupport, source.getDocumentElement (), prefixResolver); // επιστροφή του προκύπτοντος κόμβος return list.nodeset (). item (0); } 

Ο παραπάνω κώδικας μπορεί να μην φαίνεται πολύ καλύτερος από την προηγούμενη δοκιμή, αλλά τα περισσότερα από τα περιεχόμενα αυτής της μεθόδου θα μπορούσαν να ενσωματωθούν σε μια βοηθητική τάξη. Το μόνο μέρος που αλλάζει ξανά και ξανά είναι η πραγματική έκφραση XPath και ο κόμβος προορισμού.

Αυτό μας επιτρέπει να δημιουργήσουμε ένα XPathHelper τάξη, που μοιάζει με αυτό:

εισαγωγή org.w3c.dom. *; εισαγωγή org.xml.sax. *; εισαγωγή org.apache.xalan.xpath. *; εισαγωγή org.apache.xalan.xpath.xml. *; δημόσια τάξη XPathHelper {XMLParserLiaison xpathSupport = null; XPathProcessor xpathParser = null; PrefixResolver prefixResolver = null; XPathHelper () {xpathSupport = νέο XMLParserLiaisonDefault (); xpathParser = νέο XPathProcessorImpl (xpathSupport); } δημόσια διαδικασία NodeListXPath (String xpath, Node target) μέσω SAXException {prefixResolver = new PrefixResolverDefault (target); // δημιουργήστε το XPath και αρχικοποιήστε το XPath xp = νέο XPath (); xpathParser.initXPath (xp, xpath, prefixResolver); // τώρα εκτελέστε την εντολή XPath select XObject list = xp.execute (xpathSupport, target, prefixResolver); // επιστρέψτε τον προκύπτοντα κόμβο return list.nodeset (); }} 

Μετά τη δημιουργία της τάξης βοηθών, μπορούμε να ξαναγράψουμε ξανά τη μέθοδο εύρεσης, η οποία είναι τώρα πολύ σύντομη:

δημόσιος κόμβος findAddress (όνομα συμβολοσειράς, πηγή εγγράφου) ρίχνει την εξαίρεση {XPathHelper xpathHelper = νέο XPathHelper (); NodeList nl = xpathHelper.processXPath ("// address [child :: addressee [text () = '" + name + "']]", source.getDocumentElement ()); επιστροφή nl.item (0); } 

Η βοηθητική κλάση μπορεί τώρα να χρησιμοποιηθεί όποτε ένας κόμβος ή ένα σύνολο κόμβων πρέπει να βρίσκεται σε ένα δεδομένο έγγραφο XML. Η πραγματική δήλωση XPath θα μπορούσε ακόμη και να φορτωθεί από μια εξωτερική πηγή, έτσι ώστε οι αλλαγές να μπορούσαν να γίνουν εν κινήσει εάν αλλάξει η δομή του εγγράφου προέλευσης. Σε αυτήν την περίπτωση, δεν απαιτείται μεταγλώττιση.

Επεξεργασία εγγράφων XML με φύλλα στυλ XSL

Σε ορισμένες περιπτώσεις, είναι λογικό να αναθέτετε σε ολόκληρο τον χειρισμό ενός εγγράφου XML σε ένα εξωτερικό φύλλο στυλ XSL, μια διαδικασία από ορισμένες απόψεις παρόμοια με τη χρήση του XPath όπως περιγράφεται στην προηγούμενη ενότητα. Με τα φύλλα στυλ XSL, μπορείτε να δημιουργήσετε ένα έγγραφο εξόδου επιλέγοντας κόμβους από το έγγραφο εισαγωγής και συγχωνεύοντας το περιεχόμενό τους με περιεχόμενο φύλλου στυλ, με βάση τους κανόνες μοτίβου.

Εάν μια εφαρμογή αλλάξει τη δομή και το περιεχόμενο ενός εγγράφου XML και παράγει ένα νέο έγγραφο, ίσως είναι καλύτερο και ευκολότερο να χρησιμοποιήσετε ένα φύλλο στυλ για να χειριστείτε την εργασία αντί να γράψετε ένα πρόγραμμα Java που κάνει την ίδια δουλειά. Το φύλλο στυλ πιθανότατα αποθηκεύεται σε εξωτερικό αρχείο, επιτρέποντάς σας να το αλλάξετε εν κινήσει, χωρίς να χρειάζεται να μεταγλωττιστεί ξανά.

Για παράδειγμα, θα μπορούσαμε να ολοκληρώσουμε την επεξεργασία για το βιβλίο διευθύνσεων δείγμα δημιουργώντας ένα φύλλο στυλ που συγχωνεύει την προσωρινά αποθηκευμένη έκδοση του βιβλίο διευθύνσεων με το ενημερωμένο, δημιουργώντας έτσι ένα νέο έγγραφο με τις ενημερώσεις σε αυτό.

Εδώ είναι ένα δείγμα ενός τέτοιου φύλλου στυλ:

   //mymachine.com/changed.xml 
$config[zx-auto] not found$config[zx-overlay] not found