Использование схемы для изменения порядка элементов XML-документа в соответствии со схемой - PullRequest
5 голосов
/ 17 сентября 2009

Скажем, у меня есть XML-документ (представленный в виде текста, W3C DOM и т. Д.), А также XML-схема. В документе XML есть все правильные элементы, определенные в схеме, но в неправильном порядке.

Как использовать схему для «переупорядочивания» элементов в документе в соответствии с порядком, определенным схемой?

Я знаю, что это должно быть возможно, возможно, с использованием XSOM , поскольку генератор кода JAXB XJC аннотирует свои сгенерированные классы с правильным порядком сериализации элементов.

Однако я не знаком с API-интерфейсом XSOM, и он довольно плотный, поэтому я надеюсь, что один из вас имеет некоторый опыт работы с ним и может указать мне правильное направление. Что-то вроде «какие дочерние элементы разрешены внутри этого родительского элемента и в каком порядке?»


Позвольте мне привести пример.

У меня есть такой XML-документ:

<A>
   <Y/>
   <X/>
</A>

У меня есть XML-схема, которая говорит, что содержимое <A> должно быть <X>, за которым следует <Y>. Теперь ясно, что если я попытаюсь проверить документ по схеме, он потерпит неудачу, поскольку <X> и <Y> находятся в неправильном порядке. Но я знаю, что мой документ заранее «неправильный», поэтому я пока не использую схему для проверки. Однако я делаю знаю, что в моем документе есть все правильные элементы, определенные схемой, только в неправильном порядке.

Что я хочу сделать, так это программно изучить схему (возможно, используя XSOM - объектную модель для XML-схемы) и спросить ее, каким должно быть содержимое <A>. API предоставит информацию о том, что «вам нужно <X>, за которым следует <Y>».

Итак, я беру свой XML-документ (используя DOM API) и реорганизую его, и, соответственно, теперь документ будет проверен по схеме.

Важно понимать, что XSOM здесь - это java API, который представляет информацию, содержащуюся в XML-схеме, , а не информацию, содержащуюся в моем экземпляре документа.

Что я не хочу делать, так это генерировать код из схемы, поскольку схема неизвестна во время сборки. Кроме того, XSLT бесполезен, так как правильное упорядочение элементов определяется исключительно словарем данных, содержащимся в схеме.

Надеюсь, теперь это достаточно явно.

Ответы [ 4 ]

3 голосов
/ 26 ноября 2011

Я застрял с той же проблемой около двух недель. Наконец я получил прорыв. Это может быть достигнуто с помощью функции маршалинга / демаршаллинга JAXB.

В JAXB marshal / unmarshal, проверка XML является дополнительной функцией. Поэтому при создании объектов Marshaller и UnMarshaller мы не вызываем метод setSchema (схема). Пропуск этого шага исключает возможность проверки XML маршала / демаршала.

Итак,

  1. Если какой-либо обязательный элемент согласно XSD отсутствует в XML, он упускается из виду.
  2. Если какой-либо тег, отсутствующий в XSD, присутствует в XML, ошибка не выдается, и он не присутствует в новом XML, полученном после маршалинга / демаршаллинга.
  3. Если элементы не в последовательности, они переупорядочиваются. Это делается с помощью сгенерированных JAXB POJO, которые мы передаем при создании JAXBContext.
  4. Если элемент неуместен внутри какого-либо другого тега, он пропускается в новом XML. При сортировке / отмене сортировки ошибки не выдаются.

public class JAXBSequenceUtil {
  public static void main(String[] args) throws JAXBException, IOException {

    String xml = FileUtils.readFileToString(new File(
            "./conf/out/Response_103_1015700001&^&IOF.xml"));

    System.out.println("Before marshalling : \n" + xml);
    String sequencedXml = correctSequence(xml,
            "org.acord.standards.life._2");
    System.out.println("After marshalling : \n" + sequencedXml);
  }

  /**
   * @param xml
   *            - XML string to be corrected for sequence.
   * @param jaxbPackage
   *            - package containing JAXB generated classes using XSD.
   * @return String - xml with corrected sequence
   * @throws JAXBException
   */
  public static String correctSequence(String xml, String jaxbPackage)
        throws JAXBException {
    JAXBContext jaxbContext = JAXBContext.newInstance(jaxbPackage);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    Object txLifeType = unmarshaller.unmarshal(new InputSource(
            new StringReader(xml)));
    System.out.println(txLifeType);

    StringWriter stringWriter = new StringWriter();
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.marshal(txLifeType, stringWriter);

    return stringWriter.toString();
  }
}
3 голосов
/ 17 сентября 2009

У меня пока нет хорошего ответа на этот вопрос, но я должен отметить, что здесь есть вероятность двусмысленности. Рассмотрим эту схему:

<xs:element name="root">
  <xs:choice>
    <xs:sequence>
      <xs:element name="foo"/>
      <xs:element name="bar">
        <xs:element name="dee">
        <xs:element name="dum">
      </xs:element>
    </xs:sequence>
    <xs:sequence>
      <xs:element name="bar">
        <xs:element name="dum">
        <xs:element name="dee">
      </xs:element>
      <xs:element name="foo"/>
    </xs:sequence>
  </xs:choice>
</xs:element>

и этот входной XML:

<root>
  <foo/>
  <bar>
    <dum/>
    <dee/>
  </bar>
</root>

Это можно сделать, чтобы соответствовать схеме, либо переупорядочив <foo> и <bar>, либо переупорядочив <dee> и <dum>. Кажется, нет никаких причин предпочитать одно другому.

2 голосов
/ 17 сентября 2009

Ваша проблема заключается в следующем: у вас есть файл XSM, который не соответствует схеме, и вы хотите преобразовать его во что-то, что является допустимым.

С помощью XSOM вы можете прочитать структуру в XSD и, возможно, проанализировать XML, но для этого все равно потребуется дополнительное отображение из недопустимой формы в допустимую форму. Использовать таблицу стилей было бы намного проще, потому что вы бы шли по XML, используя узлы XPath для обработки элементов в правильном порядке. С XML, где вы хотите яблоки перед груши, таблица стилей сначала скопирует узел яблока (/ Fruit / Apple), прежде чем он скопирует узел груши. Таким образом, независимо от порядка в старом файле, они будут в правильном порядке в новом файле.

Что вы можете сделать с XSOM - это прочитать XSD и сгенерировать таблицу стилей, которая будет переупорядочивать данные. Затем преобразуйте XML, используя эту таблицу стилей. После того как XSOM сгенерирует таблицу стилей для XSD, вы можете просто повторно использовать таблицу стилей, пока XSD не будет изменен или не потребуется другой XSD.

Конечно, вы можете использовать XSOM для немедленного копирования узлов в правильном порядке. Но так как это означает, что ваш код должен пройти через все узлы и дочерние узлы, процесс может занять некоторое время для завершения. Таблица стилей будет делать то же самое, но преобразователь сможет обрабатывать все это быстрее. Он может работать непосредственно с данными, в то время как код Java должен был бы получать / устанавливать каждый узел через свойства XMLDocument.


Итак, я бы использовал XSOM для генерации таблицы стилей для XSD, которая просто скопировала бы узел XML за узлом повторно использовать снова и снова. Таблицу стилей нужно будет переписывать только при изменении XSD, и она будет работать быстрее, чем когда Java API должен проходить через сами узлы. Таблица стилей не заботится о порядке, поэтому она всегда будет в правильном порядке.
Чтобы сделать ее более интересной, вы можете просто пропустить XSOM и попробовать поработать с таблицей стилей, которая читает XSD, чтобы сгенерировать другую таблицу стилей из Это. Эта сгенерированная таблица стилей будет копировать узлы XML в точном порядке, как определено в таблице стилей. Это будет сложно? Фактически, таблица стилей должна была бы генерировать шаблоны для каждого элемента и удостовериться, что дочерние элементы в этом элементе обработаны в правильном порядке.

Когда я думаю об этом, мне интересно, было ли это уже сделано раньше. Он будет очень общим и сможет обрабатывать практически все XSD / XML.

Давайте посмотрим ... Используя "// xsd: element / @ name", вы получите все имена элементов в схеме. Каждое уникальное имя должно быть переведено в шаблон. В этих шаблонах вам нужно будет обработать дочерние узлы определенного элемента, который немного сложнее получить. Элементы могут иметь ссылку, которой вы должны следовать. В противном случае получите все дочерние узлы xsd: element it.

1 голос
/ 17 сентября 2009

По сути, вы хотите взять корневой элемент и оттуда рекурсивно посмотреть на дочерние элементы в документе и на дочерние элементы, определенные в схеме, и установить порядок соответствия.

Я дам вам C # -синтаксическое решение, так как это то, что я кодирую днем ​​и ночью, оно довольно близко к Java. Обратите внимание, что мне придется догадываться о XSOM, так как я не знаю, что это API. Я также создал методы XML Dom, так как предоставление ваших C # методов, вероятно, не помогло бы:)

// предполагаем, что первый вызов SortChildrenIntoNewDocument (sourceDom.DocumentElement, targetDom.DocumentElement, schema.RootElement)

public void SortChildrenIntoNewDocument( XmlElement source, XmlElement target, SchemaElement schemaElement )
{
    // whatever method you use to ask the XSOM to tell you the correct contents
    SchemaElement[] orderedChildren = schemaElement.GetChildren();
    for( int i = 0; i < orderedChildren.Length; i++ )
    {
        XmlElement sourceChild = source.SelectChildByName( orderedChildren[ i ].Name );
        XmlElement targetChild = target.AddChild( sourceChild )
        // recursive-call
        SortChildrenIntoNewDocument( sourceChild, targetChild, orderedChildren[ i ] );
    }
}

Я бы не рекомендовал рекурсивный метод, если это будет глубокое дерево, в этом случае вам придется создавать объекты типа «обходчик деревьев». Преимущество этого подхода заключается в том, что вы сможете обрабатывать более сложные вещи, например, когда схема говорит, что у вас может быть 0 или более элементов, вы можете продолжать обрабатывать исходные узлы до тех пор, пока больше не будет совпадений, затем переместите обходчик схемы. оттуда.

...