groovy.xml.XmlUtil.serialize能否为空节点添加xsi:nil="true"?
Great question! Let's break this down step by step to solve your XML empty node handling issue:
一、XmlUtil.serialize本身无法直接实现
First off, let's be clear: groovy.xml.XmlUtil.serialize doesn't have built-in configuration to automatically add xsi:nil="true" to empty nodes. It only compresses empty tags like <n:test></n:test> into self-closing ones <n:test/>—no way to tweak this behavior directly. So we need extra processing steps, and the XSLT approach you mentioned is perfect for this.
二、使用XSLT处理XML字符串(匹配你提供的模板)
Java and Groovy fully support XSLT transformations using the JDK's built-in javax.xml.transform APIs. Here's a Groovy code example that uses your exact XSLT template to process the XML string from your StringBuilder:
import javax.xml.transform.TransformerFactory import javax.xml.transform.stream.StreamSource import javax.xml.transform.stream.StreamResult import java.io.StringReader import java.io.StringWriter // Your XML payload built from StringBuilder def xmlPayload = '''<root xmlns:n="http://example.com/ns"> <n:test></n:test> <n:value>Hello</n:value> <n:empty/> </root>''' // Your provided XSLT template def xsltTemplate = '''<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <xsl:output method="xml" indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="*[not(text())]"> <xsl:copy> <xsl:attribute name="xsi:nil">true</xsl:attribute> </xsl:copy> </xsl:template> </xsl:stylesheet>''' // Execute XSLT transformation def transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader(xsltTemplate))) def writer = new StringWriter() transformer.transform(new StreamSource(new StringReader(xmlPayload)), new StreamResult(writer)) def processedXml = writer.toString() println(processedXml)
This code will convert all empty nodes (both <n:test></n:test> and <n:empty/>) to <n:test xsi:nil="true"/> and <n:empty xsi:nil="true"/>, exactly as you need. If your XML uses namespaces, the XSLT will retain them and automatically add the xsi namespace declaration to the root node if it's missing—fully compliant with XML standards.
三、Alternative: Manual DOM Traversal
If you prefer not to use XSLT, you can use Groovy's DOM manipulation to iterate through nodes and add the attribute manually:
import groovy.xml.XmlParser import groovy.xml.XmlNodePrinter def xmlPayload = '''<root xmlns:n="http://example.com/ns"> <n:test></n:test> <n:value>Hello</n:value> <n:empty/> </root>''' def parser = new XmlParser() def root = parser.parseText(xmlPayload) // Traverse all nodes and check for empty content root.depthFirst().each { node -> // Check if node has no text and no child nodes if (!node.text() && node.children().isEmpty()) { // Ensure xsi namespace exists in root if (!root.namespaceMap.containsKey('xsi')) { root.namespaceMap['xsi'] = 'http://www.w3.org/2001/XMLSchema-instance' } node.attributes()['xsi:nil'] = 'true' } } // Serialize the modified XML def writer = new StringWriter() new XmlNodePrinter(new PrintWriter(writer)).print(root) def processedXml = writer.toString() println(processedXml)
This method gives you more granular control, which is useful if you have special rules for specific nodes.
Wrap-Up
XmlUtil.serializecan't addxsi:nildirectly—extra processing is required- XSLT is the cleanest, most universal solution, matching the behavior you're familiar with from Microsoft's XSL preprocessor, and it's natively supported in Java/Groovy
- Manual DOM traversal is a good option for custom node-specific logic
内容的提问来源于stack exchange,提问作者user7071176

