JAXB разбирает несколько XML-элементов в один класс

У меня есть следующая структура XML, которая моделирует единую концепцию для нескольких элементов XML. Этот формат мне не подвластен.

<Output>
  <Wrapper>
    <Channel>
      <id>1</id>
      <type>x</type>
    </Channel>
    <Channel>
      <id>2</id>
      <type>y</type>
    </Channel>
    <ChannelName>
      <id>1</id>
      <name>Channel name</name>
    </ChannelName>
    <ChannelName>
      <id>2</id>
      <name>Another channel name</name>
    </ChannelName>
  </Wrapper>
</Output>

Я хочу смоделировать это в базе данных, над которой у меня есть контроль, и которая может иметь более простую таблицу Channel с полями id, type и name. Поэтому я хотел бы разделить на один List<Channel> в классе Wrapper.

Можно ли это сделать с аннотациями @Xml... автоматически? В настоящее время я использую JAXB для разделения на отдельные списки классов @XmlElement(name="Channel") и @XmlElement(name="ChannelName"), а затем постобрабатываю переходный ChannelName/name в Channel, но я думаю, что должен быть более простой автоматизированный способ сопоставления этих элементов. Или это работа для XSLT?

Может быть полезно узнать, что XML поступает в виде файла HTTP POST, и я использую Spring 3, Java и Hibernate. Я надеюсь, что-то в EclipseLink JAXB (MOXy) может помочь :)


person andyb    schedule 16.03.2011    source источник


Ответы (2)


Вы можете сэкономить время кодирования, автоматизировав этот процесс в JAXB:

Создайте XML-схему для вашего XML, используя приведенную ниже ссылку, и сохраните ее как файл output.xsd: http://www.xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx

Запустите файл пакетного сценария (назовите его output.bat) ниже из корневой папки проекта (.) с помощью JDK, поскольку только JDK имеет инструмент xjc.exe (заполните необходимые реквизиты):

"C:\Program Files\Java\jdk1.6.0_24\bin\xjc.exe" -p %1 %2 -d %3

где...

syntax: output.bat %1 %2 %3
%1 = target package name
%2 = full file path name of the generated XML schema .xsd 
%3 = root source folder to store generated JAXB java files

Пример:

скажем, папка проекта организована следующим образом:

.
\_src

Запустите в командной строке из (.):

output.bat com.project.xml .\output.xsd .\src

Он создаст несколько файлов:

.
\_src
  \_com
    \_project
      \_xml
        |_ObjectFactory.java
        |_Output.java

Затем вы можете создать несколько полезных методов ниже для управления Output объектами:

private JAXBContext jaxbContext = null;
private Unmarshaller unmarshaller = null;
private Marshaller marshaller = null;

public OutputManager(String packageName) {
    try {
        jaxbContext = JAXBContext.newInstance(packageName);
        unmarshaller = jaxbContext.createUnmarshaller();
        marshaller = jaxbContext.createMarshaller();
    } catch (JAXBException e) {
    }
}

public Output loadXML(InputStream istrm) {

    Output load = null;

    try {
        Object o = unmarshaller.unmarshal(istrm); 

        if (o != null) {

            load = (Output) o;

        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
    return load;
}

public void saveXML(Object o, java.io.File file) {

    Output save = null;

    try {
        save = (Output) o;

        if (save != null) {
            marshaller.marshal(save, file);
        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
}

public void saveXML(Object o, FileOutputStream ostrm) {

    Output save = null;

    try {

        save = (Output) o;

        if (save != null) {
            marshaller.marshal(save, ostrm);
        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
}
person eee    schedule 16.03.2011
comment
Это полезно, но я не думаю, что это решит мою проблему, поскольку я действительно хочу писать как можно меньше кода, но при этом манипулировать структурой XML во время связывания. Пишу как можно меньше кода не потому, что я ленив, я просто действительно считаю, что это не нужно, а также добавляет сложности и требует более тщательного тестирования. Простой, элегантный код делает меня счастливым. Однако +1 за ответ, так как я не знал об упомянутой вами ссылке или инструменте. - person andyb; 17.03.2011
comment
@andyb: Может быть, вы можете сначала автоматизировать, а затем редактировать сгенерированные файлы. - person eee; 17.03.2011

person    schedule
comment
Надеялся, что вы будете на @Blaise :) Я просмотрел XmlPath, но не смог понять, как найти имя для данного идентификатора. Итак, когда я распаковываю первый Channel, XPath, который я хочу получить, называется ChannelName[id="1"]/name, но не вижу, как подключить id. Я посмотрю ваш блог прямо сейчас. - person andyb; 16.03.2011
comment
@andyb — в настоящее время мы работаем над связанной функцией (см. bugs.eclipse.org/339596) . Начальная фаза нацелена на условия для атрибута (т. е. ChannelName[@id=1]/Name). Вам нужно маршалировать или просто демаршалировать? - person bdoughan; 16.03.2011
comment
@Blaise - мне нужно только распаковать, и похоже, что функция предиката может быть решением, поскольку мне нужно получить данные из элементов в другом месте документа? Кроме того, я просмотрел ваш (отличный) блог и почти заставил MOXy работать в Spring. Мне потребовалось некоторое время, чтобы понять файлы jaxb.properties и jaxb.index. В настоящее время я получаю исключение class org.springframework.oxm.jaxb.Jaxb2Marshaller$ByteArrayDataSource requires a zero argument constructor. Я использую Eclipse Persistence Services 2.3.0.v20110312-r9123. - person andyb; 16.03.2011
comment
Итак, я решил ошибку конструктора с нулевым аргументом. Это произошло из-за моего непонимания того, что должно быть в файле jaxb.index, и я также перечислил свой класс XMLHelper! См. Руководство по MOXy Spring JAXB Annotations, если кто-то хочет узнать пример I следил. - person andyb; 16.03.2011
comment
Большое спасибо за это @Blaise. На данный момент я далеко от своего кода, но попробую это завтра. Я мог бы быть почти готов с предложенным вами предикативным решением. Я планирую сначала запустить XML через простой XSLT, чтобы добавить атрибуты, необходимые для связи элементов. - person andyb; 16.03.2011
comment
XSLT — это хороший подход, который полностью интегрируется с JAXB: 5118189" title="как заставить jaxb игнорировать определенные данные во время десортировки"> stackoverflow.com/questions/5112404/ - person bdoughan; 16.03.2011
comment
Спасибо за ссылку XSLT и обновление ответа. Я подозревал, что для этого могут потребоваться некоторые вспомогательные объекты. Я решил использовать XSLT, чтобы преобразовать XML в более красивую структуру перед привязкой, в основном потому, что я хорошо знаю XSLT/XPath, но он также не требует дополнительных объектов и менее сложен. Я не думаю, что функция сопоставления предикатов MOXy подходит, поскольку мне нужно комбинировать элементы XML на основе совпадения значения атрибута переменной в предикате. т. е. для Channel@id=1 I нужно будет найти ChannelName[@id="1"]/Name, а затем другой элемент для @id=2 - person andyb; 17.03.2011
comment
Тем не менее, я все равно буду использовать MOXy, так как мне нравится аннотация @XmlPath. Я также буду следить за его развитием. Я просто думаю, что пытаюсь связать и реорганизовать очень плоскую структуру XML, в то же время это слишком сложно без использования вспомогательных объектов или преобразования. Я думал, что смогу уйти с некоторыми очень умными аннотациями. В любом случае, еще раз спасибо и ответ принят :-) - person andyb; 17.03.2011
comment
@andyb — теперь доступна функция предиката, для получения дополнительной информации см.: bdoughan.blogspot.com/2011/03/ - person bdoughan; 22.03.2011