Vue d'ensemble
Les documents XML sont hiérarchique des documents, où les mêmes noms d'élément et les espaces de noms peuvent se produire à plusieurs endroits, avoir différents sens, et dans infinitif profondeur (récursif). Comme d'habitude, la solution à de gros problèmes, est de les diviser en petits problèmes. Dans le contexte de l'analyse XML, cela signifie que l'analyse des parties spécifiques du langage XML dans des méthodes spécifiques à XML. Par exemple, un morceau de la logique serait d'analyser une adresse:
<Address>
<Street>Odins vei</Street>
<Buildning>4</Buildning>
<Door>b</Door>
</Address>
c'est à dire que vous auriez une méthode
AddressType parseAddress(...); // A
ou
void parseAddress(...); // B
quelque part dans votre logique, en prenant XML entrées arguments et retourne un objet (le résultat de B peut être récupérée dans un champ plus tard).
SAX
SAX "pousse" XML événements, laissant à vous de déterminer où l'XML événements appartiennent dans votre programme / données.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// .. your logic here for start element
}
Dans le cas d'un Buildning' élément de début, vous aurez besoin de déterminer que vous êtes en fait l'analyse d'un discours, puis la route de l'événement XML pour la méthode dont le travail consiste à interpréter Adresse.
StAX
StAX 'tire' XML événements, laissant à vous de déterminer où dans votre programme / données pour recevoir le XML événements.
// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
// .. your logic here for start element
}
Bien sûr, vous voulez toujours recevoir un " Buildning de l'événement dans la méthode dont le travail consiste à interpréter Adresse.
Discussion
La différence entre SAX et StAX est que de pousser et tirer. Dans les deux cas, l'analyse de l'état doit être traitée d'une certaine manière.
Cela se traduit par la méthode B comme typique de SAX, et Une méthode pour StAX. En outre, SAX doit donner B format XML des événements, alors que StAX peut donner de multiples événements (par le passage d'un XMLStreamReader exemple).
Ainsi B vérifiez d'abord l'état précédent de l'analyse et de gérer chaque événement XML et ensuite stocker l'état (dans un champ). Méthode Un peut juste gérer le XML événements tout à la fois par l'accès au XMLStreamReader plusieurs fois jusqu'à satisfaction.
Conclusion
StAX vous permet de structurer votre analyse (liaison de données) code en fonction de la structure XML; en rapport avec le SAX, l '"état" est implicite à partir du flux de programme pour StAX, alors que dans SAX, vous avez toujours besoin de préserver une sorte de variable d'état + route du flux en fonction de cet état, pour la plupart des cas des appels.
Je recommande StAX pour tous, mais le plus simple des documents. Plutôt passer à SAX comme une optimisation plus tard (mais vous aurez probablement envie d'aller binaire).
Suivez ce modèle lors de l'analyse à l'aide de StAX:
public MyDataBindingObject parse(..) { // provide input stream, reader, etc
// set up parser
// read the root tag to get to level 1
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
// check if correct root tag
break;
}
// add check for document end if you want to
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
// read root attributes if any
int level = 1; // we are at level 1, since we have read the document header
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// do stateful stuff here
// for child logic:
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --; // read from level 1 to 0 in submethod.
// do something with the result of subtree
object.setWhatever(child);
}
// alternatively, faster
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --; // read from level 1 to 0 in submethod.
// do something with the result of subtree
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
return object;
}
Ainsi, le submethod utilise la même approche, à savoir le comptage de niveau:
private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySubTreeObject object = new MySubTreeObject();
// read element attributes if any
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// do stateful stuff here
// for child logic:
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --; // read from level 1 to 0 in submethod.
// use subtree object somehow
object.setWhatever(child);
}
// alternatively, faster, but less strict
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --; // read from level 1 to 0 in submethod.
// use subtree object somehow
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
return object;
}
Et puis finalement vous atteignez un niveau dans lequel vous pourrez lire les types de base.
private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySetterGetterObject object = new MySetterGetterObject();
// read element attributes if any
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
// assume <FirstName>Thomas</FirstName>:
if(reader.getLocalName().equals("FirstName")) {
// read tag contents
String text = reader.getElementText()
if(text.length() > 0) {
object.setName(reader.getElementText())
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
// etc ..
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
// do stateful stuff here, too
}
} while(level > 0);
return object;
}
C'est assez simple et il n'y a pas de place pour des malentendus. Rappelez-vous juste pour décrémenter le niveau correctement:
A. après avoir attendu des personnages, mais il a obtenu un END_ELEMENT dans certains balise qui devrait contenir des caractères (dans le schéma ci-dessus):
<Name>Thomas</Name>
a la place
<Name></Name>
Le même est vrai pour un manque de sous-arborescence trop, vous obtenez l'idée.
B. après l'appel de subparsing méthodes, qui sont appelés sur démarrer éléments, et les retours APRÈS la fin de l'élément, c'est à dire l'analyseur se trouve à un niveau plus bas qu'avant l'appel de la méthode (le schéma ci-dessus).
Notez comment cette approche ignore totalement 'ignorable' les espaces en trop, pour les plus robustes de la mise en œuvre.
Edit: Ajout d'une vue d'ensemble, ajout de l'exemple, a ajouté le type de retour dans le modèle exemple. Ajouté plus robuste de la manipulation de texte.