2016-12-08 4 views
0

Ich beginne gerade mit JAXB heute, und ich bin auf einer ungeraden Darstellung einer Liste von Datenelementen fest, wenn es nur einen Wert gibt. Beachten Sie, dass es für einzelne Werte von colors eher als Element statt als Liste behandelt wird und nicht in einem -Tag eingeschlossen ist. Die Daten stammen von einer externen Quelle und ich habe keine Kontrolle über die Formatierung.JAXB Parsing Wrapper mit einem einzelnen Textelement anstelle von XmlElement

Wie kann JAXB mit beiden Darstellungen von colors umgehen?

<?xml version="1.0" encoding="utf-8"?> 
<widgets> 
    <widget> 
     <name>SingleValue</name> 
     <colors>Blue</colors> 
    </widget> 
    <widget> 
     <name>ListValues</name> 
     <colors> 
      <color>Red</color> 
      <color>Blue</color> 
     </colors> 
    </widget> 
</widgets> 

Ich habe mit Kombinationen von @XmlElementWrapper verschiedene Versuche versucht und @XmlElement, @XmlAnyElements, @XmlElementRef(s) und @XmlMixed. Ich habe sogar eine Farbklasse erstellt und mehrere Zuordnungen zu Arrays und Strings ohne Glück versucht; Sie würden einzeln arbeiten, aber nicht, wenn sie gleichzeitig verwendet werden.

Mit dem obigen XML-Beispiel ist hier ein einfaches Programm, das "Blau" korrekt analysiert, wenn es in color Tags verpackt wurde. Momentan gibt dieses Programm eine leere Liste für Farben zurück und kann "Blau" nicht aufnehmen.

@XmlRootElement(name = "widgets") 
@XmlAccessorOrder(XmlAccessOrder.UNDEFINED) 
public class Widgets { 
    private List<Widget> widgets = new ArrayList<Widget>(); 
    public static void main(String[] args) { 
     File f = new File("C:\\aersmine\\AERS_KDR_Data", "widgets.xml"); 
     try { 
      Widgets widgets = Widgets.load(f); 

      for (Widget widget : widgets.widgets) { 
       StringBuilder sb = new StringBuilder(); 
       for (String color : widget.getColors()) { 
        if (sb.length() > 0) 
         sb.append(", "); 
        sb.append(color); 
       } 
       System.out.println("Widget " + widget.getName() + " Colors: " + sb.toString()); 
      } 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static Widgets load(File file) 
      throws JAXBException, IOException { 
     FileInputStream is = new FileInputStream(file); 
     try { 
      JAXBContext ctx = JAXBContext.newInstance(Widgets.class); 
      Unmarshaller u = ctx.createUnmarshaller(); 
      return (Widgets) u.unmarshal(is); 
     } 
     finally { 
      is.close(); 
     } 
    } 
    @XmlElement(name="widget") 
    public List<Widget> getWidgets() { 
     return widgets; 
    } 
    public void setWidgets(List<Widget> widgets) { 
     this.widgets = widgets; 
    } 
} 

public class Widget { 
    public String n; 
    public List<String> cl = new ArrayList<String>(); 

    @XmlElement(name="name") 
    public String getName() { 
     return n; 
    } 
    public void setName(String name) { 
     this.n = name; 
    } 

    @XmlElementWrapper(name="colors") 
    @XmlElement(name="color") 
    public List<String> getColors() { 
     return cl; 
    } 
    public void setColors(List<String> colors) { 
     this.cl = colors; 
    } 
} 

Vielen Dank für Ihre Hilfe.

Antwort

0

In erster Linie ist es wichtig für mich zu sagen, dass dies nicht die Antwort ist, die ich suche, aber es ist eine temporäre/alternative Lösung, bis eine JAXB Lösung gefunden wurde. Ich bin derzeit gezwungen, diese Lösung zu verwenden, bis eine JAXB-Lösung gefunden werden kann.

Ich biete diese alternative Lösung, da andere es nützlich finden können, da es die Möglichkeit bietet, einen regulären Ausdruck zu verwenden, um den Stream zu manipulieren und das zugrunde liegende Problem zu korrigieren, das verhindert, dass das ursprüngliche XML korrekt analysiert wird. Dies wird durch die Verwendung eines FilterReaders erreicht.

Als einfache Zusammenfassung enthalten die XML-Daten eine Liste von Farben, die von colors umschlossen sind. Jede Farbe ist mit color wie erwartet in der Liste markiert. Das Problem ist, wenn es einen einzelnen Farbwert gibt; Dieser Wert ist nicht in color eingeschlossen und daher nicht analysierbar.

Beispiel für eine gute Liste der Farben:

<colors> 
    <color>Red</color> 
    <color>Blue</color> 
</colors> 

Beispiel für eine schlechte Einzelfarben:

<colors>Blue</colors> 

Diese Lösung wird ein Muster für reguläre Ausdrücke verwenden, <colors>([^<>]+?)\s*<\/colors>, die falsche XML-Liste zu identifizieren . Dann wird ein Ersetzungszeichenfolgenwert <color>|</color> verwendet, um dem gefundenen Objekt group(1), das auf das Pipe-Zeichen aufteilt, ein Präfix und Suffix zuzuweisen.

Die korrigierten Ergebnisse für die schlechte einfarbige dann werden wie folgt so der JAXB unmarshalling es in ziehen wird:

<colors><color>Blue</color></colors> 

Umsetzung:

den Code oben in der ursprünglichen Anforderung verwenden, ersetzen die Funktion mit diesem. Beachten Sie, dass neben der Hinzufügung der neuen WidgetFilterReader die andere wichtige Änderung in dieser Version des Loaders die Verwendung einer FileReader ist.

public static Widgets load(File file) 
      throws JAXBException, IOException 
    { 
     Reader reader = 
      new WidgetFilterReader( 
        "<colors>([^<>]+?)\\s*<\\/colors>", "<color>|</color>", 
       new FileReader(file)); 
     try 
     { 
      JAXBContext ctx = JAXBContext.newInstance(Widgets.class); 
      Unmarshaller u = ctx.createUnmarshaller(); 
      return (Widgets) u.unmarshal(reader); 
     } 
     finally 
     { 
      reader.close(); 
     } 
    } 

Dann diese Klasse hinzufügen, die die Filterreader Implementierung ist:

public class WidgetFilterReader 
    extends FilterReader 
{ 
    private StringBuilder sb = new StringBuilder(); 

    @SuppressWarnings("unused") 
    private final String search; 
    private final String replace; 
    private Pattern pattern; 
    private static final String EOF = "\uFFEE"; // half-width white circle - Used as to place holder and token 

    /** 
    * 
    * @param search A regular expression to build the pattern. Example: "<colors>([^<>]+?)\\s*<\\/colors>" 
    * @param replace A String value with up to two parts to prefix and suffix the found group(1) object, separated by a pipe: ie |. 
    *   Example: "<color>*</color>" 
    * @param in 
    */ 
    protected WidgetFilterReader(String search, String replace, Reader in) { 
     super(in); 
     this.search = search; 
     this.replace = replace; 
     this.pattern = Pattern.compile(search); 
    } 

    @Override 
    public int read() 
      throws IOException { 
     int read = ingest(); 
     return read; 
    } 

    private int ingest() throws IOException 
    { 
     if (sb.length() == 0) { 
      int c = super.read(); 
      if (c < 0) 
       return c; 
      sb.append((char) c); 
     } 

     if (sb.length() > 0 && sb.charAt(0) == '<') { 
      int count = 0; 
      for (int i = 0; i < sb.length(); i++) { 
       if (sb.charAt(i) == '>') 
        count++; 
      } 
      int c2; 
      while ((c2 = super.read()) >= 0 && count < 2) { 
       sb.append((char) c2); 
       if (c2 == '>') 
        count++; 
      } 
      if (c2 < 0) 
       sb.append(EOF); 
      else 
       sb.append((char) c2); 

      Matcher m = pattern.matcher(sb.toString()); 
      if (m.find(0)) { 
       String grp = m.group(1); 
       int i = sb.indexOf(grp); 
       if (i >= 0) { 
        int j = i + grp.length(); 
        String[] r = replace.split("\\|"); 
        sb.replace(i, j, (r.length > 0 ? r[0] : "") + grp + (r.length > 1 ? r[1] : "")); 
       } 
      } 
     } 

     int x = sb.charAt(0); 
     sb.deleteCharAt(0); 

     if (x == EOF.charAt(0)) 
      return -1; 
     return x; 
    } 

    @Override 
    public int read(char[] cbuf, int off, int len) 
      throws IOException { 
     int c; 
     int read = 0; 

     while (read < len && (c = ingest()) >= 0) { 
      cbuf[off + read] = (char) c; 
      read++; 
     } 
     if (read == 0) 
      read = -1; 
     return read; 
    } 
} 

Überblick darüber, wie das funktioniert:

Grundsätzlich ist diese Klasse eines Stringbuilder als Puffer verwendet, während es voraus liest die Suche nach das mitgelieferte Muster. Wenn das Muster im StringBuilder-Puffer gefunden wird, wird der StringBuilder so geändert, dass er die korrigierten Daten enthält. Dies funktioniert, da der Stream immer gelesen und dem internen Puffer hinzugefügt und dann aus diesem Puffer gezogen wird, wenn er stromaufwärts verbraucht wird. Dies stellt sicher, dass das Muster gefunden werden kann, indem nur die minimale Menge an Zeichen vor dem Upstream-Verbrauch dieser Zeichen geladen wird.

Da die EndOfFile bei der Suche nach dem Muster gefunden werden kann, muss ein Token in den Puffer eingefügt werden, damit der korrekte EOF zurückgegeben werden kann, wenn die Upstream-Verbraucher diesen Punkt erreichen. Daher die Verwendung eines ziemlich obskuren Unicode-Zeichens, das für das EOF-Token verwendet wird. WENN das in Ihren Quelldaten vorkommen könnte, sollte stattdessen etwas anderes verwendet werden.

Ich sollte auch beachten, dass, obwohl das reguläre Ausdruck Muster in dieses FilterReader übergeben wird, der Code, der genügend Daten vorfährt, um eine gültige Suche nach den Zieldaten durchzuführen, auf das spezifische Attribut des Musters, das ist verwendet werden. Es stellt sicher, dass vor dem Versuch, eine find(0), dass genügend Daten in den StringBuilder-Puffer geladen wurden. Dies wird erreicht, indem nach einem Anfangszeichen von < gesucht wird, wobei dann sichergestellt wird, dass zwei weitere > Zeichen geladen werden, um die minimalen Bedürfnisse für das gegebene Muster zu erfüllen. Was bedeutet das? Wenn Sie versuchen, diesen Code für einen anderen Zweck wiederzuverwenden, müssen Sie möglicherweise den Vorablesefehler ändern, um sicherzustellen, dass genügend Daten im Speicher für die erfolgreiche Verwendung des Mustervergleichers verfügbar sind.

+0

Ich markiere dies als die Antwort in Anbetracht der Tatsache, dass ich noch nie eine vernünftige Lösung gefunden habe. In Anbetracht dessen, dass es keine anderen Kommentare oder Lösungen gegeben hat, kann ich daher nur annehmen, dass es keinen Standard gibt, mit schlechtem XML-Design mit jaxb umzugehen. Ich möchte immer noch eine bessere Lösung finden. Vielen Dank. – Scottt

Verwandte Themen