根据属性值移除 XML 节点

Posted

技术标签:

【中文标题】根据属性值移除 XML 节点【英文标题】:Remove XML node based on attribute value 【发布时间】:2021-08-28 19:38:32 【问题描述】:

我有以下 XML 文件,如果 EventType 匹配开始或分配,我试图从中删除整个 AuditTrailEntry 节点。我在 *** 上看到过类似的情况,但该解决方案对我不起作用,我总是收到错误消息 - NOT_FOUND_ERR: 如果 oldChild 不是该节点的子节点,则引发。你知道如何解决这个问题吗?

<AuditTrailEntry>
                <Data>
                    <Attribute name="elementId">sid-9E1E4EAD-5182-482F-9D5C-10961CA5BE2D</Attribute>
                    <Attribute name="processId">sid-C83E31C0-4CCC-4763-90C2-E09F1537D1EC</Attribute>
                </Data>
                <WorkflowModelElement>Test</WorkflowModelElement>
                <EventType>assign</EventType>
                <Timestamp>2021-06-17T13:20:10.557+00:00</Timestamp>
            </AuditTrailEntry>
            <AuditTrailEntry>
                <Data>
                    <Attribute name="elementId">sid-9E1E4EAD-5182-482F-9D5C-10961CA5BE2D</Attribute>
                    <Attribute name="processId">sid-C83E31C0-4CCC-4763-90C2-E09F1537D1EC</Attribute>
                    <Attribute name="resourceId">sid-B0EBDEE1-4649-4AB6-B9AD-159188A0A02E</Attribute>
                </Data>
                <WorkflowModelElement>Test</WorkflowModelElement>
                <EventType>start</EventType>
                <Timestamp>2021-06-17T13:56:47.003+00:00</Timestamp>
                <Originator>Test-000002</Originator>
            </AuditTrailEntry>
            <AuditTrailEntry>
                <Data>
                    <Attribute name="elementId">sid-9E1E4EAD-5182-482F-9D5C-10961CA5BE2D</Attribute>
                    <Attribute name="processId">sid-C83E31C0-4CCC-4763-90C2-E09F1537D1EC</Attribute>
                    <Attribute name="resourceCost">0.8558947222222222</Attribute>
                    <Attribute name="resourceId">sid-B0EBDEE1-4649-4AB6-B9AD-159188A0A02E</Attribute>
                </Data>
                <WorkflowModelElement>Test</WorkflowModelElement>
                <EventType>complete</EventType>
                <Timestamp>2021-06-17T14:01:27.114+00:00</Timestamp>
                <Originator>Test-000002</Originator>
            </AuditTrailEntry>
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class MainClass 


    public static void main(String[] args) throws Exception 
        File f = new File("input.mxml");
        Document XML_DOC = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f);
        NodeList entries = XML_DOC.getElementsByTagName("AuditTrailEntry");
        //printDocument(XML_DOC);


        for (int i = 0; i < entries.getLength(); i++) 
            Node entry = entries.item(i);
            if(elementStatus(entry)) 
                XML_DOC.getChildNodes().item(0).removeChild(entry);

        

//        System.out.println("");System.out.println("");
//        System.out.println("------------------------------------");
//        printDocument(XML_DOC);
    

    public static void printDocument(Document doc) throws IOException, TransformerException 
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("http://xml.apache.org/xsltindent-amount", "4");


        transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(System.out, "UTF-8")));
    

    public static boolean elementStatus(Node node) 
        if (node.getNodeType() == Node.ELEMENT_NODE) 
            Element e = (Element) node;
            if (e.getElementsByTagName("EventType").item(0).getTextContent().contentEquals("start"))
                return true;
        
        return false;
    

```

【问题讨论】:

(1) 提供的输入 XML 格式不正确。它缺少一个根节点。 (2) 对于此类任务,最好使用 XSLT 对不起,我只展示了重要的节点。还有两个节点和根节点。 【参考方案1】:

最好使用 XSLT 来完成此类任务。

XML 文件应具有根节点的良好格式。

XSLT 有所谓的 Identity Transform 模式。

XSLT

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

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

    <!--<xsl:template match="AuditTrailEntry[EventType=('assign','start')]"/>-->
    <xsl:template match="AuditTrailEntry[EventType='assign' or EventType='start']"/>
</xsl:stylesheet>

Java

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;

public class Process 
    public static void main(String[] args) 
        String XSLTFILE = "e:/Temp/Process.xslt";
        String INFILE = "e:/Temp/Input.xml";
        String OUTFILE = "e:/Temp/Output.xml";

        try 
            // I/O
            StreamSource input = new StreamSource(new File(INFILE));
            StreamSource xslt = new StreamSource(new File(XSLTFILE));
            StreamResult output = new StreamResult(new File(OUTFILE));

            // Transformation
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer(xslt);
            transformer.transform(input, output);
         catch (TransformerConfigurationException tce) 
            tce.printStackTrace();
         catch (TransformerException te) 
            te.printStackTrace();
         catch (Exception e) 
            e.printStackTrace();
        
    

【讨论】:

您好,谢谢您的回答。我试过这个,但我总是在 处遇到语法错误 - 我将 xpath 行编辑为 "//AuditTrailEntry[EventType= ('assign','start')]" 因为它还有两个节点,但它仍然不起作用。 @Alina,很抱歉造成混淆。我修复了 XSLT。请检查一下。这是 XPath 2.0 与 Xpath 1.0 的冲突。【参考方案2】:

使用 XPath,事情变得容易多了:

    XPath xpath = XPathFactory
            .newInstance()
            .newXPath();

    String expression = "//AuditTrailEntry[EventType='assign' or EventType='start']";
    NodeList list = (NodeList) xpath.evaluate(expression, XML_DOC, XPathConstants.NODESET);

    for (int i=0; i < list.getLength(); ++i) 
        Node target = list.item(i);
        target.getParentNode().removeChild(target);
    

【讨论】:

以上是关于根据属性值移除 XML 节点的主要内容,如果未能解决你的问题,请参考以下文章

如何使用xsl根据属性将xml的兄弟节点更改为父节点和子节点

如何根据标签的属性名称选择两个标签之间的 xml 文件中的所有节点?

java中dom方式读取xml,怎样根据xml的某个属性找到指定节点?? 比如id属性

C# XML 如何直接根据属性值 读取 对应的值 怎么写???

根据属性和节点值提取节点

使用xslt和c#从中选择​​一个xml节点并根据其值添加更多节点[重复]