2016-12-08 8 views
0

今日はJAXBを使い始めたばかりです。値が1つしかない場合、データ要素のリストが奇妙に表示されます。 colorsという単一の値の場合は、リストの代わりに要素として扱われ、colorタグで囲まれていないことに注意してください。データは外部ソースから取得されており、書式設定を制御することはできません。JAXB XmlElementの代わりに単一テキスト要素を解析するラッパー

JAXBは、colorsの両方の表現をどのように扱うことができますか?

<?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> 

私は@XmlElementWrapper@XmlElement@XmlAnyElements@XmlElementRef(s)、および@XmlMixedの組み合わせで様々な試みを試みました。私は色のクラスを作成し、配列や文字列に複数のマッピングを試みました。それらは個別に動作しますが、同時に使用される場合は動作しません。

上記のサンプルXMLを使用して、colorタグでラップされている場合、「青」を正しく解析する簡単なプログラムを次に示します。現在、このプログラムは空の色のリストを返すので、「青」をピックアップすることはできません。

@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; 
    } 
} 

ご協力いただきありがとうございます。

答えて

0

私が探しているのは答えではありませんが、JAXBソリューションが見つかるまでは一時的な/代替の解決策です。私は現在、JAXBソリューションが見つかるまでこのソリューションを使用しなければなりません。

正規表現パターンを使用してストリームを操作し、元のXMLが正しく解析されないという根本的な問題を修正する機能を備えているため、他の人が役に立ちそうなので、この代替ソリューションを提供しています。これは、FilterReaderの使用によって実現されます。

単純な要約として、XMLデータには、colorsで囲まれた色のリストが含まれています。各色はリスト内で期待どおりcolorとタグ付けされています。問題は、単一のカラー値がある場合です。その値はcolorにラップされていないため、解析できません。色の良いリストの

例:悪い単一色の

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

例:

<colors>Blue</colors> 

このソリューションは、正しくないXMLリストを識別するために、正規表現パターン、<colors>([^<>]+?)\s*<\/colors>を使用します。 。次に、置換文字列値<color>|</color>を使用して、見つかったgroup(1)オブジェクトをパイプ文字で分割する接頭辞と接尾辞を適用します。

ので、JAXBのアンマーシャルがそれを引っ張ってくる次のように悪い単一色の修正の結果は、その後になります:置き換え、元の要求に上記のコードを使用して

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

実装public static Widgets loadはこれと機能します。新しいWidgetFilterReaderの追加に加えて、このバージョンのこのローダーでのもう1つの重要な変更点は、FileReaderの使用です。これがどのように機能するかについて

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; 
    } 
} 

概要:

それは先を探して読み込みながら、基本的に、このクラスはバッファとしてのStringBuilderを使用している

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(); 
     } 
    } 

はその後FilterReaderの実装で、このクラスを追加します供給されたパターン。パターンがStringBuilderバッファに見つかると、StringBuilderは修正されたデータを含むように変更されます。これは、ストリームが常に読み込まれ、内部バッファに追加され、ストリームから消費されるときにそのバッファから引き出されるためです。これにより、これらの文字の上流使用の前に最小量の文字をロードするだけでパターンを見つけることができます。

パターンを検索しているときにEndOfFileが検出される可能性があるため、アップストリームコンシューマがそのポイントに到達するときに正しいEOFが返されるように、バッファに挿入するトークンが必要です。したがって、EOFトークンに使用されるむしろあいまいなUnicode文字の使用。ソースデータ内に存在する可能性がある場合は、代わりに別のものを使用する必要があります。

正規表現パターンがこのFilterReaderに渡されているにもかかわらず、ターゲットデータの有効な検索を実行するのに十分なデータをプリフェッチするコードは、パターンの特定の属性使用されています。これは、find(0)を試行する前に、十分なデータがStringBuilderバッファにロードされていることを保証します。これは、最初の文字が<であることを確認してから、与えられたパターンの最小限のニーズを満たすために2つ以上の>文字がロードされることを保証することによって達成される。どういう意味ですか?このコードを別の目的で再利用しようとしている場合は、プリフェッチャを修正してパターンマッチャーが正常に使用するために十分なデータをメモリに確保する必要があります。

+0

私は合理的な解決策を見つけたことがないと考えて答えをマークしています。他のコメントや解決策がないことを考えれば、私は、jaxbを使って貧弱なXML設計に対処する標準的な方法がないと仮定することができます。私はもっ​​と良い解決策を探しています。ありがとう。 – Scottt

関連する問題