我有一个输出XML,它包含空元素和空元素,但带有属性。
我检查了一些较旧的帖子,它帮助我解决了部分问题。
我使用了以下XSLT解决方案
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|node()">
<xsl:if test=". != '' or ./@* != ''">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if> </xsl:template> </xsl:stylesheet>
但问题是删除具有属性的子元素的元素,如
<CurrencyList>
<Currency code="EURO"/>
<Currency code="USD"/>
</CurrencyList>
任何人都知道如何解决这个问题?
非常感谢
答案 0 :(得分:1)
就像删除空目录一样:你必须进行深度优先递归遍历:如果删除了所有子目录,那么可以考虑删除当前目录。
因此,删除最好用Java进行递归。优点是,不需要副本。
的代码强> 的
根据要求,使用XML API是非常零碎的,一些未经测试的代码:
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
public class XmlCleanup {
public static void main(String[] args) {
if (args.length == 0) {
args = new String[] { "/home/joop/Labortablo/test1.xml" };
}
new XmlCleanup().process(args[0]);
}
public void process(String xmlPath) {
try {
// Read XML document:
DocumentBuilder builder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new File(xmlPath));
removeEmptyChildElements(doc.getDocumentElement());
// Write XML document back:
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(xmlPath
.replaceFirst("\\.xml$", "") + "-clean.xml"));
transformer.transform(source, result);
} catch (TransformerException ex) {
Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
} catch (ParserConfigurationException ex) {
Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void removeEmptyChildElements(Element parentElement) {
List<Element> toRemove = new LinkedList<Element>();
NodeList children = parentElement.getChildNodes();
int childrenCount = children.getLength();
for (int i = 0; i < childrenCount; ++i) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
removeEmptyChildElements(childElement);
if (elementIsRedundant(childElement)) {
toRemove.add(childElement);
}
}
}
for (Element childElement: toRemove) {
parentElement.removeChild(childElement);
}
parentElement.normalize();
}
private boolean elementIsRedundant(Element element) {
if (element.hasAttributes())
return false;
if (!element.hasChildNodes())
return true;
NodeList children = element.getChildNodes();
int childrenCount = children.getLength();
for (int i = 0; i < childrenCount; ++i) {
Node child = children.item(i);
String value = child.getNodeValue();
if (value != null && !value.matches("\\s*")) {
return false; // Found non-whitespace text
}
}
return true;
}
}
它使用java.xml.transform
,因此您也可以使用XSLT转换;更简单的是使用javax.xml.stream.XMLOutputFactory
。
答案 1 :(得分:0)
我认为你从身份变换开始就走在正确的轨道上。我建议保持身份模板不变,然后添加一个忽略空元素的更具体的模板。
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
对'normalize-space`的调用使得此模板折叠所有连续的空格,通常仅用于缩进。然后匹配的第二部分排除所有具有属性本身或具有属性后代的元素。出于调试目的,只要元素被删除,我就让模板在输出中创建注释。
<xsl:template match="*[normalize-space(.) = '' and not(descendant-or-self::*/@*)]">
<xsl:comment><xsl:value-of select="name(.)" /></xsl:comment>
</xsl:template>