首页 > 解决方案 > 使用 StAX 对 XML 进行简单修改

问题描述

我试图修改现有的 XML 文件。据我所知,不可能直接即时执行,所以我的想法是读取流中的文件,修改它,创建一个新文件,然后用新文件替换旧文件。
我只需要应用简单的更改,因此我决定采用 StAX 方法,因为它更适合大量数据或简单处理。

现有的 XML 文件:

<?xml version='1.0' encoding='UTF-8'?>
<Company>
    <Employee>
        <FirstName>Tanmay</FirstName>
        <LastName>Patil</LastName>
        <ContactNo>1234567890</ContactNo>
        <Address>
            <City>Bangalore</City>
            <State>Karnataka</State>
            <Zip>560212</Zip>
        </Address>
    </Employee>
</Company>

期望的输出:

<?xml version='1.0' encoding='UTF-8'?>
<Company>
    <Employee>
        <FirstName>Tanmay</FirstName>
        <LastName>Patil</LastName>
        <ContactNo>1234567890</ContactNo>
        <Address>
            <City>Bangalore</City>
            <State>Karnataka</State>
            <NewElem>Some value</NewElem> <!-- Replacing all ZIP-elements -->
        </Address>
    </Employee>
</Company>

这段代码只是复制了一个 XML 文件(source):

public static void writeAll(XMLStreamReader xmlr, XMLStreamWriter writer)
        throws XMLStreamException {
    while (xmlr.hasNext()) {
        write(xmlr, writer);
        xmlr.next();
    }
    write(xmlr, writer); // write the last element
    writer.flush();
}

public static void write(XMLStreamReader xmlr, XMLStreamWriter writer) throws XMLStreamException {
    switch (xmlr.getEventType()) {
        case XMLEvent.START_ELEMENT:
            final String localName = xmlr.getLocalName();
            final String namespaceURI = xmlr.getNamespaceURI();
            if (namespaceURI != null && namespaceURI.length() > 0) {
                final String prefix = xmlr.getPrefix();
                if (prefix != null) {
                    writer.writeStartElement(prefix, localName, namespaceURI);
                } else {
                    writer.writeStartElement(namespaceURI, localName);
                }
            } else {
                writer.writeStartElement(localName);
            }

            for (int i = 0, len = xmlr.getNamespaceCount(); i < len; i++) {
                writer.writeNamespace(xmlr.getNamespacePrefix(i), xmlr.getNamespaceURI(i));
            }

            for (int i = 0, len = xmlr.getAttributeCount(); i < len; i++) {
                String attUri = xmlr.getAttributeNamespace(i);
                if (attUri != null) {
                    writer.writeAttribute(attUri, xmlr.getAttributeLocalName(i), xmlr.getAttributeValue(i));
                } else {
                    writer.writeAttribute(xmlr.getAttributeLocalName(i), xmlr.getAttributeValue(i));
                }
            }
            break;
        case XMLEvent.END_ELEMENT:
            writer.writeEndElement();
            break;
        case XMLEvent.SPACE:
        case XMLEvent.CHARACTERS:
            writer.writeCharacters(xmlr.getTextCharacters(), xmlr.getTextStart(), xmlr.getTextLength());
            break;
        case XMLEvent.PROCESSING_INSTRUCTION:
            writer.writeProcessingInstruction(xmlr.getPITarget(), xmlr.getPIData());
            break;
        case XMLEvent.CDATA:
            writer.writeCData(xmlr.getText());
            break;
        case XMLEvent.COMMENT:
            writer.writeComment(xmlr.getText());
            break;
        case XMLEvent.ENTITY_REFERENCE:
            writer.writeEntityRef(xmlr.getLocalName());
            break;
        case XMLEvent.START_DOCUMENT:
            String encoding = xmlr.getCharacterEncodingScheme();
            String version = xmlr.getVersion();
            if (encoding != null && version != null) {
                writer.writeStartDocument(encoding, version);
            } else if (version != null) {
                writer.writeStartDocument(xmlr.getVersion());
            }
            break;
        case XMLEvent.END_DOCUMENT:
            writer.writeEndDocument();
            break;
        case XMLEvent.DTD:
            writer.writeDTD(xmlr.getText());
            break;
    }
}

它有效,但我不确定write()-method. 那些开关盒真的有必要吗?


我也遇到了问题,将 ZIP-Elements 替换为

while (reader.hasNext()) {
    write(reader, writer);
    reader.next();
    if (reader.getEventType() == XMLStreamReader.START_ELEMENT) {
        String elementName = reader.getLocalName();
        if (elementName.contains("ZIP")) {
                writer.writeStartElement("newElem");
                writer.writeAttribute("atr", "val");
                writer.writeEndElement();
        }
    }
}

替换 XML 文件中的某些节点的最有效方法是什么?

标签: javaxmlstax

解决方案


XSLT 有所谓的身份转换模式。

有用的链接:XSL 身份转换

下面的 XSLT 将按原样复制整个输入 XML,但 Zip 元素除外。找到 Zip 元素的那一刻,它将被新的所需标签替换。

您所要做的就是从您的Java 代码中调用XSLT 转换。

输入 XML

<?xml version="1.0" encoding="UTF-8"?>
<Company>
    <Employee>
        <FirstName>Tanmay</FirstName>
        <LastName>Patil</LastName>
        <ContactNo>1234567890</ContactNo>
        <Address>
            <City>Bangalore</City>
            <State>Karnataka</State>
            <Zip>560212</Zip>
        </Address>
    </Employee>
</Company>

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" method="xml" encoding="utf-8"/>

    <!-- IdentityTransform -->
    <xsl:template match="/ | @* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Zip">
        <NewElem>Some value</NewElem>
    </xsl:template>

</xsl:stylesheet>

输出 XML

<?xml version='1.0' encoding='utf-8' ?>
<Company>
  <Employee>
    <FirstName>Tanmay</FirstName>
    <LastName>Patil</LastName>
    <ContactNo>1234567890</ContactNo>
    <Address>
      <City>Bangalore</City>
      <State>Karnataka</State>
      <NewElem>Some value</NewElem>
    </Address>
  </Employee>
</Company>

推荐阅读