Manipulate XML Comments Using JAXB

I need to read an XML file and comment on or uncomment some of its elements based on some conditions. The file starts as follows:

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

 <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 string that matches the conditions.
Unfortunately, this behavior is necessary and cannot be changed.

+7
java xml xml-parsing jaxb
source share
4 answers

Comment is a special type of node. You cannot "switch" from / to the state with comments / without commenting. I see at least the possibilities here, both without JAXB, though:

DOM Method:

  • Parse the XML file using the DOM parser of your choice ( with setIgnoringComments(false) )
  • Get the source data from each node (see Comment . GetData () )
  • Create a new node from the line
  • Replace the "comment" node with the new node (see Node.replaceChild )

Feel free to ask if you need a more detailed answer. You should easily find extensive documentation for each step.

XSLT Method:

You can also use XSLT, as @Xavier noted in the comments. The problem here is that pure coincidence and substitution output the contents of the comment as unshielded text and will not recognize it as real XML data. You can use saxon to get around this, I suppose, with something like this:

 <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> 
+2
source share

I think that reading comments and without comments makes this problem harder. An easier way is to add an attribute with which you can activate the tag or deactivate it. No workaround is required, you just need to mark it true or false.

Example:

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

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

can convert 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 may be an optimized way to solve this problem. Now you can use JAXB and mark the item active or inactive instead of comments and comments.

If this does not make your life easier, there is always a workaround using regex, xslt, etc.

+5
source share

For such a need, I would explicitly suggest XSLT , since XML transformation and XSLT were created to transform the XML content.

Then i used a template style sheets intended for use as a string format as follows:

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

  • if you select normal , it will just copy the contents of node
  • if you select comment , he will comment on his content

So, if we activate element1 , element3 and element5 , the real content of our stylesheet will be 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 can use a different library if you wish, XSLT is the standard, so it will work anyway.

 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 readability, I formatted the output

+4
source share

I don't think this is possible using JAXB cleanly. The following is an example of using the STAX API . I used a similar implementation when I needed to manipulate 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 xml example you specified, and currently supports switching one element. You can expand it to switch multiple items.

The above code also uses the following event filter

 class DocElementEventFilter implements EventFilter { @Override public boolean accept(XMLEvent event) { return !(event.isStartDocument() || event.isEndDocument()); } } 

Hope this helps you.

+3
source share

All Articles