使用JAXB处理XML注释

7

我需要读取一个XML文件,并根据一些条件对其中的元素进行注释或取消注释。 文件的开头如下:

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

如果我想要激活element1element3element5,文件应该如下所示:
<elements>
    <element1 atribute="value"/>
    <!-- <element2 atribute="value"/> -->
    <element3 atribute="value"/>
    <!-- <element4 atribute="value"/> -->
    <element5 atribute="value"/>
</elements>

换句话说,我正在寻找一种方法来添加或删除每行XML中符合条件的<!-- -->标签。
不幸的是,这种行为是必需的,无法更改。

1
改进了代码和文本格式,并添加了一些解释。 - matteodv
4个回答

5

我认为阅读有注释和无注释会使这个问题变得复杂。更简单的方法是添加属性,通过该属性您可以激活或停用标签。不需要任何解决方法,只需要将其标记为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并标记元素为活动或非活动状态,而不是注释和取消注释。

如果这不能让您的生活更轻松,总还有使用正则表达式、XSLT等的解决方法。


1
请仔细阅读 OP 的问题,明确指出我们不能改变行为,我引用“不幸的是,这种行为是必需的,无法更改。” - Nicolas Filotto
尼古拉是正确的。我对XML没有控制权。我必须根据这些不成熟的规格更改值。 - Viorel Florian

4

对于这种需求,我会明确建议使用XSLT,因为它是一种XML转换,而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>

如您所见,有两种模式:

  1. 如果选择normal,它将简单地复制节点的内容
  2. 如果选择comment,它将注释其内容

因此,如果我们激活element1element3element5,我们样式表的实际内容将为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:为了易读性,我格式化了输出

请注意:上述内容提醒读者为了易读性进行了格式化处理。

3

我认为单纯使用JAXB是无法实现的。以下是使用STAX API实现的方法。我曾在需要操作XML注释时使用了类似的实现。

    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, "$1");

                 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());
    }
}

希望这能帮到你。

2
评论是一种特殊类型的节点。您无法从/到注释/取消注释状态切换。我至少看到了两种可能性,但都不使用JAXB:

DOM方式:

  1. 使用您选择的DOM解析器解析XML文件(with setIgnoringComments(false))
  2. 从每个节点获取原始数据(请参阅Comment.getData())
  3. 从字符串创建一个新节点
  4. 使用您的新节点替换“注释”节点(请参阅Node.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>

这正是我担心的。我真的想使用纯JAXB来实现这一点。我不想切换,我愿意添加这样的注释,因为我知道需要注释什么和需要设置什么。 - Viorel Florian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接