简体   繁体   English

使用JAXB处理XML注释

[英]Manipulate XML comments with JAXB

I need to read an XML file and comment or uncomment some elements to it based on some conditions. 我需要读取一个XML文件,并根据某些条件对其中的某些元素进行注释或取消注释。 The file starts off like this: 该文件如下所示:

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

If I want to activate element1 , element3 and element5 , the file should look like this: 如果我想激活element1element3element5 ,文件应如下所示:

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

In other words, I'm looking for a way to add or remove <!-- --> tags from each XML line that meets the conditions. 换句话说,我正在寻找一种方法来添加或删除符合条件的每个XML行的<!-- -->标记。
Unfortunately, this behavior is needed, and cannot be changed. 不幸的是,这种行为是必需的,无法改变。

I think reading commented and uncommented makes this problem complex. 我认为阅读评论和未评论会使这个问题变得复杂。 Simpler way would be adding attribute by which you can either activate tag or deactivate. 更简单的方法是添加属性,您可以通过该属性激活标记或停用。 No workaround would be required just you would require to mark it true or false. 只要您要求将其标记为真或假,就不需要解决方法。

For Example: 例如:

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

could be transformed into to. 可以转化为。

<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>

Similarly, below 同样,下面

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

could be transformed into to. 可以转化为。

<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>

This could be optimized way of solving this problem. 这可以是解决此问题的优化方法。 Now, you might use JAXB and mark element active or inactive in stead of commenting and un-commenting. 现在,您可以使用JAXB并将元素标记为活动或非活动,而不是注释和取消注释。

If this doesn't makes your life easier there is always workaround using regex, xslt etc.. 如果这不能让您的生活更轻松,那么总是使用正则表达式,xslt等进行解决方法。

For such need I would clearly suggest XSLT as it is somehow an XML transformation and XSLT has been created to transform XML content. 对于这样的需求,我会清楚地建议XSLT因为它在某种程度上是XML transformation ,并且已经创建了XSLT来转换XML内容。

I would then use a template of a stylesheet that is meant to be used as a String format like this: 然后,我将使用样式表的模板 ,该模板用作String格式,如下所示:

<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>

As you can see there are 2 modes: 如您所见,有两种模式:

  1. if you choose normal it will simply copy the content of the node 如果你选择normal它只会复制节点的内容
  2. if you choose comment it will comment its content 如果您选择comment ,它将评论其内容

So in case we activate element1 , element3 and element5 , the real content of our stylesheet will be String.format(template, "normal", "comment", "normal", "comment", "normal") 因此,如果我们激活element1element3element5 ,我们样式表的真实内容将是String.format(template, "normal", "comment", "normal", "comment", "normal")

In the code snippet below, I use jcabi-xml as it is very easy to use but you are free to use another library if you wish, XSLT is a standard so it will still work. 在下面的代码片段中,我使用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());

Output: 输出:

<?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: For the sake of readability, I formatted the output 注意:为了便于阅读,我格式化了输出

I do not think it is achievable using JAXB purely. 我不认为纯粹使用JAXB是可以实现的。 Here is a way to achieve using STAX API . 这是一种使用STAX API实现的方法。 I used similar implementation where i needed to manipulate XML comments 我使用了类似的实现,我需要操作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, "$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();

This is very specific to the example xml you have given and currently support toggle of one element. 这非常特定于您给出的示例xml,并且当前支持切换一个元素。 You can extend it to toggle multiple elements as well. 您可以扩展它以切换多个元素。

Above code also uses following Event Filter 上面的代码也使用以下事件过滤器

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

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

Hope this helps you. 希望这对你有所帮助。

A comment is a special type of node. 注释是一种特殊类型的节点。 You cannot "toggle" from/to a commented/uncommented state. 您无法从/向注释/未注释状态“切换”。 I see at least too possibilities here, both without JAXB only though : 我在这里看到至少太多的可能性,两者都没有JAXB,但是:

The DOM way : DOM方式:

  1. Parse the XML file with a DOM parser of your choice ( with setIgnoringComments(false) ) 使用您选择的DOM解析器解析XML文件( with setIgnoringComments(false)
  2. Get the raw data from each node (see Comment . getData() ) 从每个节点获取原始数据(请参阅注释 .getData()
  3. Create a new node from the string 从字符串创建一个新节点
  4. Replace the "comment" node with your new node (see Node.replaceChild ) 用新节点替换“comment”节点(参见Node.replaceChild

Don't hesitate to ask if you need a more detailed answer. 不要犹豫,询问您是否需要更详细的答案。 You should easily find extensive documentation for each step. 您应该很容易找到每个步骤的大量文档。

The XSLT way : XSLT方式:

You could also use XSLT, as @Xavier pointed out in the comments. 您也可以使用XSLT,正如@Xavier在评论中指出的那样。 The issue here is that a pure match and replace will output the comment's content as unescaped text and won't recognize it as real XML data. 这里的问题是纯匹配和替换会将注释的内容输出为未转义的文本,并且不会将其识别为真正的XML数据。 You can use saxon to circumvent this I suppose, with something like that : 我可以使用撒克逊来规避这种情况,例如:

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM