Управление комментариями XML с помощью JAXB

мне нужно прочитать XML-файл и прокомментировать или раскомментировать некоторые элементы в нем на основе некоторых условий. Файл начинается так:

<elements>
    <!-- <element1 atribute="value"/> -->
    <!-- <element2 atribute="value"/> -->
    <!-- <element3 atribute="value"/> -->
    <!-- <element4 atribute="value"/> -->
    <!-- <element5 atribute="value"/> -->
</elements>

если я хочу активировать element1, element3 и element5, то файл должен выглядеть так:

<elements>
    <element1 atribute="value"/>
    <!-- <element2 atribute="value"/> -->
    <element3 atribute="value"/>
    <!-- <element4 atribute="value"/> -->
    <element5 atribute="value"/>
</elements>

другими словами, Я ищу способ добавить или удалить <!-- --> теги из каждой строки XML, которая соответствует условиям.
К сожалению, такое поведение необходимо, и не может быть изменен.

4 ответов


комментарий - это особый тип узла. Вы не можете "переключаться" из/в комментарии/государство комментарии. Я вижу здесь, по крайней мере, слишком много возможностей, как без JAXB, хотя :

путь DOM:

  1. проанализируйте XML-файл с помощью парсера DOM по вашему выбору (with setIgnoringComments(false))
  2. получить исходные данные с каждого узла (см. комментарий.getData ())
  3. создать новый узел из строки
  4. заменить узел" комментарий" с вашим новым узлом (см. узел.replaceChild)

Не стесняйтесь спрашивать, нужен ли вам более подробный ответ. Вы должны легко найти обширную документацию для каждого шага.

путь XSLT:

вы также можете использовать XSLT, как указал @Xavier в комментариях. Проблема здесь в том, что чистое совпадение и замена выведут содержимое комментария в виде неэскапированного текста и не распознают его как реальные XML-данные. Вы можете использовать saxon, чтобы обойти это Я полагаю, с чем-то вроде этого :

<xsl:template match="comment()[contains(., 'your conditional match')]">
    <xsl:variable name="comment" select="saxon:parse(.)" as="document-node()"/>
    <xsl:copy-of select="$comment"/>
</xsl:template>

Я думаю, что чтение прокомментировал и раскомментировать, делает эту проблему сложной. Более простым способом было бы добавление атрибута, с помощью которого вы можете активировать тег или деактивировать. Никакого обходного пути не потребуется, просто вам потребуется пометить его true или false.

Например:

<elements>
    <!-- <element1 atribute="value"/> -->
    <!-- <element2 atribute="value"/> -->
    <!-- <element3 atribute="value"/> -->
    <!-- <element4 atribute="value"/> -->
    <!-- <element5 atribute="value"/> -->
</elements>

может быть преобразована в.

<elements>
    <element1 atribute="value" isActive="false"/>
    <element2 atribute="value" isActive="false"/>
    <element3 atribute="value" isActive="false"/>
    <element4 atribute="value" isActive="false"/>
    <element5 atribute="value" isActive="false"/>
</elements>

аналогично, ниже

<?xml version="1.0" encoding="UTF-8"?>
<elements>
    <element1 atribute="value"/>
    <!--<element2 atribute="value"/>-->
    <element3 atribute="value"/>
    <!--<element4 atribute="value"/>-->
    <element5 atribute="value"/>
</elements>

может быть преобразована в.

<elements>
    <element1 atribute="value" isActive="true"/>
    <element2 atribute="value" isActive="false"/>
    <element3 atribute="value" isActive="true"/>
    <element4 atribute="value" isActive="false"/>
    <element5 atribute="value" isActive="true"/>
</elements>

это может быть оптимизировано путем решение этой проблемы. Теперь вы можете использовать JAXB и mark element active или inactive вместо комментариев и без комментариев.

если это не делает вашу жизнь проще, всегда есть обходной путь с помощью regex, xslt и т. д..


для такой необходимости я бы четко предложил XSLT как-то XML transformation и XSLT был создан для преобразования XML содержание.

я бы тогда использовал шаблон таблицы стилей, которая предназначена для использования в качестве строкового формата, как это:

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='2.0'>
  <xsl:template match='/'>
      <elements>
          <xsl:apply-templates select="elements/element1" mode="%s"/>
          <xsl:apply-templates select="elements/element2" mode="%s"/>
          <xsl:apply-templates select="elements/element3" mode="%s"/>
          <xsl:apply-templates select="elements/element4" mode="%s"/>
          <xsl:apply-templates select="elements/element5" mode="%s"/>
      </elements>
  </xsl:template>
  <xsl:template match='*' mode='normal'>
      <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match='*' mode='comment'>
      <xsl:text disable-output-escaping="yes">&lt;!--</xsl:text><xsl:copy-of select="."/>--<xsl:text disable-output-escaping="yes">&gt;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

как вы можете видеть есть 2 режима:

  1. если вы выберите normal он просто скопирует содержимое узла
  2. если вы выберите comment он будет прокомментируйте его содержание

Итак, если мы активируем element1, element3 и element5, реальное содержание нашей таблицы стилей будет String.format(template, "normal", "comment", "normal", "comment", "normal")

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

XML first = new XMLDocument(
    "<elements>\n" +
        "    <element1 atribute=\"value\"/>\n" +
        "    <element2 atribute=\"value\"/>\n" +
        "    <element3 atribute=\"value\"/>\n" +
        "    <element4 atribute=\"value\"/>\n" +
        "    <element5 atribute=\"value\"/>\n" +
        "</elements>"
);
String template = "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='2.0'>\n" +
    "  <xsl:template match='/'>\n" +
    "      <elements>\n" +
    "          <xsl:apply-templates select=\"elements/element1\" mode=\"%s\"/>\n" +
    "          <xsl:apply-templates select=\"elements/element2\" mode=\"%s\"/>\n" +
    "          <xsl:apply-templates select=\"elements/element3\" mode=\"%s\"/>\n" +
    "          <xsl:apply-templates select=\"elements/element4\" mode=\"%s\"/>\n" +
    "          <xsl:apply-templates select=\"elements/element5\" mode=\"%s\"/>\n" +
    "      </elements>\n" +
    "  </xsl:template>\n" +
    "  <xsl:template match='*' mode='normal'>\n" +
    "      <xsl:copy-of select=\".\"/>\n" +
    "  </xsl:template>\n" +
    "  <xsl:template match='*' mode='comment'>\n" +
    "      <xsl:text disable-output-escaping=\"yes\">&lt;!--</xsl:text><xsl:copy-of select=\".\"/>--<xsl:text disable-output-escaping=\"yes\">&gt;</xsl:text>\n" +
    "  </xsl:template>\n" +
    "</xsl:stylesheet>";
XML second = new XSLDocument(
    String.format(template, "normal", "comment", "normal", "comment", "normal")
).transform(first);
System.out.println(second.toString());

выход:

<?xml version="1.0" encoding="UTF-8"?>
<elements>
    <element1 atribute="value"/>
    <!--<element2 atribute="value"/>-->
    <element3 atribute="value"/>
    <!--<element4 atribute="value"/>-->
    <element5 atribute="value"/>
</elements>

NB: для удобства чтения, Я отформатировал вывод


Я не думаю, что это достижимо с помощью JAXB чисто. Вот способ добиться использования STAX API. Я использовал подобную реализацию, где мне нужно было манипулировать XML comments

    XMLInputFactory factory = XMLInputFactory.newInstance();

    XMLEventReader reader =factory.createXMLEventReader(new FileReader("input.xml"));

    XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(new FileWriter("out.xml"));


    String toggleMe = "element2";
    String regEx = "<!--(.*)-->";
    while(reader.hasNext()) {
        XMLEvent event = reader.nextEvent();

        if(event.getEventType() == XMLStreamConstants.COMMENT) {
            if(event.toString().contains(toggleMe)) {
                 String xmlElement = event.toString().replaceAll(regEx, "");

                 XMLEventReader elementReader = factory.createFilteredReader(factory.createXMLEventReader(new StringReader(xmlElement)), new DocElementEventFilter());
                 while(elementReader.hasNext()) {
                     writer.add(elementReader.nextEvent());
                 }
            }else {
                writer.add(event);
            }
        } else {
            writer.add(event);
        }

    }

    writer.flush();
    writer.close();
    reader.close();

Это очень специфично для примера xml, который вы дали, и в настоящее время поддерживает переключение одного элемента. Вы можете расширить его, чтобы переключать несколько элементов, а также.

выше код также использует следующий фильтр событий

class DocElementEventFilter implements EventFilter {
    @Override
    public boolean accept(XMLEvent event) {

        return !(event.isStartDocument() || event.isEndDocument());
    }
}

надеюсь, это поможет вам.