2016-12-08 10 views
0

Просто начинайте работу с JAXB сегодня, и я застрял на нечетном представлении списка элементов данных, когда есть только одно значение. Обратите внимание, что для одиночных значений colors он рассматривается скорее как элемент вместо списка и не обернут в тег color. Данные поступают из внешнего источника, и я не контролирую форматирование.JAXB Parsing Wrapper с одним текстовым элементом вместо 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> 

Это решение будет использовать шаблон регулярного выражения, <colors>([^<>]+?)\s*<\/colors>, чтобы определить неправильный список XML , Затем он будет использовать заменяющее строковое значение, <color>|</color>, чтобы применить префикс и суффикс к найденному объекту group(1), разбившемуся на символ канала.

скорректированного результаты за плохой одного цвета тогда станет следующим поэтому JAXB демаршаллинга будет тянуть его в:

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

реализации:

Используя код выше в исходном запросе, замените public static Widgets load функция с этим. Обратите внимание, что помимо добавления нового WidgetFilterReader, другим существенным изменением в этой версии загрузчика является использование FileReader.

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:

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 в качестве буфера, пока он читает вперед в поисках поставляемый образец. Когда шаблон найден в буфере StringBuilder, тогда StringBuilder изменен, чтобы содержать исправленные данные. Это работает, поскольку поток всегда считывается и добавляется во внутренний буфер, а затем извлекается из этого буфера, когда он потребляется. Это гарантирует, что шаблон можно найти, только загружая минимальное количество символов перед потоком потребления этих символов.

Поскольку EndOfFile можно встретить во время поиска шаблона, в буфере должен быть установлен токен, чтобы корректный EOF мог быть возвращен, так как пользователи, входящие в этот поток, достигли этой точки. Следовательно, используется довольно неясный символ юникода, который используется для токена EOF. ЕСЛИ это может быть в ваших исходных данных, тогда вместо этого следует использовать другое.

Следует также отметить, что, хотя шаблон регулярного выражения передается в этот фильтр, код, который предварительно забирает достаточное количество данных для выполнения действительного поиска целевых данных, привязывается к конкретному атрибуту шаблона, который является быть использованным. Это гарантирует, что до попытки сделать find(0), что достаточно данных было загружено в буфер StringBuilder. Это достигается путем проверки начального символа <, а затем для загрузки двух дополнительных символов > для удовлетворения минимальных потребностей для данного шаблона. Что это значит? Если вы пытаетесь повторно использовать этот код для другой цели, вам может потребоваться изменить prefetcher, чтобы гарантировать, что вы получите достаточное количество данных в памяти для успешного сопоставления шаблонов.

+0

Я отмечаю это как ответ, учитывая, что я никогда не находил разумного решения. Учитывая, что других комментариев или решений не было, я могу поэтому предположить, что нет стандартного способа обработки плохого XML-дизайна с помощью jaxb. Я все равно хотел бы найти лучшее решение. Благодарю. – Scottt

 Смежные вопросы

  • Нет связанных вопросов^_^