2010-01-11 6 views
6

J'ai un document XML (généré par des formes Adobe XFA), qui contient des données telles que les suivantes:XStream: Collapsing hiérarchie XML que je parse

<Position> 
    <PositionBorder> 
     <Title/> 
     <StartDate/> 
     <EndDate/> 
    </PositionBorder> 
</Position> 

Depuis ce fichier est défini ailleurs, je ne suis pas à la liberté de changer le format du XML que je reçois.

Dans mon code Java, je crée une classe Position qui contient le titre, les dates de début et de fin.

Mon problème est, quand j'utilise XStream pour analyser le fichier, il veut une classe PositionBorder pour contenir le titre et les dates. Je veux ignorer la bordure et placer tous les champs dans la classe Position.

Ce que je voudrais vraiment faire est d'utiliser quelque chose comme la méthode convertAnother pour convertir l'enfant de l'élément de position. J'ai essayé de faire juste cela et cela échoue, parce que mon PositionConverter est appelé pour le PositionBorder (quand j'appelle convertAnother).

Quelqu'un at-il des indices sur la façon de gérer la réduction de la structure d'un XML lors de l'analyse?

+0

do u doivent utiliser Xstream pour analyser xml? –

+0

Ce n'est pas une exigence, mais j'ai beaucoup de code existant investi, donc je préfère ne pas changer maintenant. – Steve

Répondre

4

Ce n'est pas très difficile à faire avec un convertisseur personnalisé. Ceci est un peu d'un long exemple, mais je l'espère, il est assez simple d'obtenir l'essentiel de ce que vous devez faire:

import com.thoughtworks.xstream.XStream; 
import com.thoughtworks.xstream.annotations.XStreamAlias; 
import com.thoughtworks.xstream.converters.Converter; 
import com.thoughtworks.xstream.converters.MarshallingContext; 
import com.thoughtworks.xstream.converters.UnmarshallingContext; 
import com.thoughtworks.xstream.io.HierarchicalStreamReader; 
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 

public final class ConverterTest { 
    public static void main(String[] args) { 
     XStream xstream = new XStream(); 
     xstream.autodetectAnnotations(true); 
     xstream.registerConverter(new PositionConverter()); 

     final Position position = new Position(); 
     position.setTitle("The Title"); 
     position.setStartDate("The Start Date"); 
     position.setEndDate("The End Date"); 

     final String xml = xstream.toXML(position); 
     System.out.println("Generated XML:"); 
     System.out.println(xml); 

     final Position genPosition = (Position) xstream.fromXML(xml); 
     System.out.println("Generated Position:"); 
     System.out.println("\tTitle: " + genPosition.getTitle()); 
     System.out.println("\tStart Date: " + genPosition.getStartDate()); 
     System.out.println("\tEnd Date: " + genPosition.getEndDate()); 
    } 

    @XStreamAlias("Position") 
    private static class Position { 
     public String getEndDate() { 
      return endDate; 
     } 

     public void setEndDate(String endDate) { 
      this.endDate = endDate; 
     } 

     public String getStartDate() { 
      return startDate; 
     } 

     public void setStartDate(String startDate) { 
      this.startDate = startDate; 
     } 

     public String getTitle() { 
      return title; 
     } 

     public void setTitle(String title) { 
      this.title = title; 
     } 

     private String title; 
     private String startDate; 
     private String endDate; 
    } 

    private static class PositionConverter implements Converter { 
     public boolean canConvert(Class clazz) { 
      return Position.class == clazz; 
     } 

     public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { 
      Position position = (Position)value; 
      writer.startNode("PositionBorder"); 

      writer.startNode("Title"); 
      writer.setValue(position.getTitle()); 
      writer.endNode(); 

      writer.startNode("StartDate"); 
      writer.setValue(position.getStartDate()); 
      writer.endNode(); 

      writer.startNode("EndDate"); 
      writer.setValue(position.getEndDate()); 
      writer.endNode(); 

      writer.endNode(); 
     } 

     public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 
      Position position = new Position(); 
      // move it to <PositionBorder> tag. 
      reader.moveDown(); 
      // now move it to <Title> tag. 
      reader.moveDown(); 
      String title = reader.getValue(); 
      position.setTitle(title); 
      reader.moveUp(); // moves back to <PositionBorder> 

      reader.moveDown(); // should move down to <StartDate> tag 
      String startDate = reader.getValue(); 
      position.setStartDate(startDate); 
      reader.moveUp(); // move back to <PositionBorder> 

      reader.moveDown(); // should move down to <EndDate> tag 
      String endDate = reader.getValue(); 
      position.setEndDate(endDate); 
      reader.moveUp(); // move back to <PositionBorder> 


      return position; 
     } 
    } 
} 

Essayez de lancer et voir ce qui se passe. Vous aurez besoin de le modifier en fonction de vos propres types, bien sûr - Je viens d'utiliser des chaînes pour tous les champs de Position (et je suis sûr que vous êtes la classe Position n'est pas imbriquée, non plus), mais convertir à partir d'une chaîne à une date (ou autre) devrait être plutôt triviale.

Une chose que vous voudrez garder à l'œil (et je ne l'aurais peut-être pas obtenu complètement dans mon exemple) correspond à vos appels reader.moveDown() et reader.moveUp(). (Et, si vous allez faire un marshalling au lieu de simplement unmarshalling - ce que je n'attends pas de votre question - vous voudrez faire correspondre vos appels writer.startNode() et writer.endNode() ainsi Cela ne causera probablement aucun problème avec cet exemple, mais je suis sûr que cela posera des problèmes si vous faites quelque chose de plus grand ou si vous traitez plusieurs fichiers avec la même instance XStream ou Converter. En outre, si vous essayez reader.moveDown() à partir d'un emplacement invalide, vous obtiendrez une exception vraiment moche - il devrait être assez évident.

J'ai dû jouer un peu avec les méthodes moveUp/moveDown pour les avoir au bon endroit, donc je suis sûr que vous aurez besoin de le tester et de l'ajuster jusqu'à ce que vous ayez ce dont vous avez besoin.

+1

dans votre code unmarshal 'String title = lecteur.getValue();' pourrait donner une erreur si les nœuds ne sont pas dans le même ordre. 'String title = reader.getAttribute (" attributeName ");' devrait le rendre plus sûr. – n002213f

0

Je trouve cette façon plus facile à utiliser:

@Override 
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 
     Position mPosition = new Position(); 
     while (reader.hasMoreChildren()) { 

      reader.moveDown(); 

      String nodeName = reader.getNodeName(); 

      if ("Title".equalsIgnoreCase(nodeName)) { 
       mPosition.setTitle(reader.getValue()); 
      } else if ("StartDate".equalsIgnoreCase(nodeName)) { 
       mPosition.setStartDate(reader.getValue()); 
      }else if ("attributeexample".equalsIgnoreCase(nodeName)) { 
       mPosition.setAttributeExample(reader.getAttribute("attrname")); 
      } 

      reader.moveUp(); 
     } 

     return mPosition; 
    }