Как проверить, имеет ли файл XML минимальную структуру (Java)? - PullRequest
0 голосов
/ 30 марта 2020

Мне нужно проверить, имеет ли файл XML (который сейчас представлен в виде строки) минимальную структуру, которая также хранится в другом файле / строке.

Небольшой пример, объясняющий, что Я имею в виду описано на этом изображении:

this image

Минимальная структура находится в верхнем правом кадре.

Template = "<A><B/><C><E></E></C></A>"
XML1 = "<A><B/><C><D></D><E/><F/></A>" //Compliant to Template: the structure is kept
XML2 = "<A><B><E/></B><C/></A>" //Not compliant to Template: E is child of B here, while E is child of C in Template
XML3 = "<A><C><E/><D/></C><F></F><B/></A>" //Compliant to Template: the order of children doesn't matter

Один Возможный подход состоит в том, чтобы преобразовать два файла XML, которые я хочу проверить, в два дерева и для каждого узла шаблона сравнить пути из root дерева, предполагая, что имена уникальны. Могу ли я использовать другие способы или библиотеки?

Ответы [ 3 ]

2 голосов
/ 30 марта 2020

Ваш возможный подход (преобразование двух XML и сравнение узлов) хорош, но вы не сравниваете пути, вы сравниваете узлы параллельно при навигации по узлам.

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

Например, вот так, используя рекурсивный метод:

public static void checkXml(String templateXml, String dataXml) throws Exception {
    DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
    Element templateRoot = domBuilder.parse(new InputSource(new StringReader(templateXml))).getDocumentElement();
    Element dataRoot = domBuilder.parse(new InputSource(new StringReader(dataXml))).getDocumentElement();
    if (! templateRoot.getNodeName().equals(dataRoot.getNodeName()))
        throw new IllegalArgumentException("Different root elements: " + dataRoot.getNodeName() +
                                                                " != " + templateRoot.getNodeName());
    checkChildren(templateRoot, dataRoot, dataRoot.getNodeName());
}
private static void checkChildren(Node templateParent, Node dataParent, String parentPath) {
    for (Node templateChild = templateParent.getFirstChild(); templateChild != null; templateChild = templateChild.getNextSibling()) {
        if (templateChild.getNodeType() == Node.ELEMENT_NODE) {
            String childPath = parentPath + "/" + templateChild.getNodeName();
            Node dataChild = getChild(dataParent, templateChild.getNodeName());
            if (dataChild == null)
                throw new IllegalArgumentException("Missing child: " + childPath);
            checkChildren(templateChild, dataChild, childPath);
        }
    }
}
private static Node getChild(Node parent, String name) {
    for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling())
        if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(name))
            return child;
    return null;
}

Тест

public static void main(String[] args) throws Exception {
    String template = "<A><B/><C><E></E></C></A>";
    String xml1 = "<A><B/><C><D></D><E/></C><F/></A>"; //Compliant to Template: the structure is kept
    String xml2 = "<A><B><E/></B><C/></A>"; //Not compliant to Template: E is child of B here, while E is child of C in Template
    String xml3 = "<A><C><E/><D/></C><F></F><B/></A>"; //Compliant to Template: the order of children doesn't matter

    test(template, xml1);
    test(template, xml2);
    test(template, xml3);
}
private static void test(String templateXml, String dataXml) throws Exception {
    try {
        checkXml(templateXml, dataXml);
        System.out.println("Ok");
    } catch (IllegalArgumentException e) {
        System.out.println(e.getMessage());
    } catch (Exception e) {
        System.out.println(e);
    }
}

Вывод

Ok
Missing child: A/C/E
Ok
2 голосов
/ 30 марта 2020

Вы можете использовать XSD, чтобы определить структуру XML и сравнить файл XML с ним. Вы можете взглянуть на javax. xml .validation.Validator;

Информация для валидатора: https://docs.oracle.com/javase/8/docs/api/javax/xml/validation/Validator.html

пример xsd https://docs.microsoft.com/en-Us/visualstudio/xml-tools/sample-xsd-file-purchase-order-schema?view=vs-2019

Простой XSD для вас:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="A">
    <xs:complexType>
      <xs:sequence>
        <xs:element minOccurs="0" type="xs:string" name="B"/>
        <xs:element minOccurs="0" name="C">
          <xs:complexType>
            <xs:sequence>
              <xs:element minOccurs="0" type="xs:string" name="E"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:element minOccurs="0" type="xs:string" name="F"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Как вы видите, я определил иерархию элементов. XSD будет игнорировать порядок B, C и F. Кроме того, вы можете определить minOccurs и maxOccurs, по умолчанию 1.

1 голос
/ 31 марта 2020

Ответ Андреаса хорош - я спал о проблеме и придумал подход DOM / SAX, который я опишу просто для интереса.

  • Разобрать минимальную структуру в дерево DOM (как в решении Андреаса)
  • SAX Разобрать входной файл следующим образом:
  • На каждом начальном теге pu sh полный путь тега к стеку (получить его, посмотрев сверху стека и добавление нового тега - стек может содержать список тегов или объединенную строку)
  • Для каждого конечного тега откройте верхнюю часть стека, найдите соответствующий узел в дереве DOM (это немного сложно, так как Document не имеет метода get by path). Если вы найдете один и у него НЕТ дочерних элементов, то удалите его из дерева, в противном случае ничего не делайте и продолжайте
  • После SAX-анализа всего ввода, если дерево DOM пусто, то ввод в порядке. Какая бы структура не осталась в дереве DOM, это недостающая часть ввода.

Недостатком является то, что вы манипулировали DOM для получения минимальной структуры, поэтому вам придется пересматривать ее каждый раз, немного дополнительной платы, если вы обрабатываете большое количество входных файлов.

В любом случае, Андреас собрал законченное кодированное решение, так что я представляю это здесь как альтернативу для общего интереса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...