【问题标题】:Using C# Regular expression to replace XML element content使用 C# 正则表达式替换 XML 元素内容
【发布时间】:2010-10-01 16:38:45
【问题描述】:

我正在编写一些处理记录 xml 数据的代码,并且我希望能够替换文档中某些元素(例如密码)的内容。我宁愿不序列化和解析文档,因为我的代码将处理各种模式。

输入文档示例:

文档#1:

   <user>
       <userid>jsmith</userid>
       <password>myPword</password>
    </user>

文档#2:

<secinfo>
       <ns:username>jsmith</ns:username>
       <ns:password>myPword</ns:password>
 </secinfo>

我希望我的输出是什么:

输出文档#1:

<user>
       <userid>jsmith</userid>
       <password>XXXXX</password>
 </user>

输出文档#2:

<secinfo>
       <ns:username>jsmith</ns:username>
       <ns:password>XXXXX</ns:password>
 </secinfo>

由于我将要处理的文档可能有多种模式,我希望提出一个很好的通用正则表达式解决方案,可以找到其中包含密码的元素并相应地屏蔽内容。

我可以使用正则表达式和 C# 解决这个问题,还是有更有效的方法?

【问题讨论】:

  • 当有许多其他优秀的工具可以做你想做的事情时,我当然会避免使用正则表达式。
  • 即使正则表达式能够(它们不能)具有多种模式,也使得更有必要使用某种形式的解析器而不是更少。

标签: c# .net xml regex parsing


【解决方案1】:

这个问题最好用 XSLT 解决:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="//password">
        <xsl:copy>
            <xsl:text>XXXXX</xsl:text>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

只要您正确处理命名空间,这将适用于两个输入。

编辑:澄清我所说的“正确处理命名空间”的意思

确保具有 ns 名称前缀的源文档具有为文档定义的命名空间,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<secinfo xmlns:ns="urn:foo">
    <ns:username>jsmith</ns:username>
    <ns:password>XXXXX</ns:password>
</secinfo>

【讨论】:

    【解决方案2】:

    我会说您最好使用 .NET XmlDocument 对象解析内容并使用 XPath 查找密码元素,然后更改它们的 innerXML 属性。它的优点是更正确(因为 XML 一开始就不是规则的),并且在概念上很容易理解。

    【讨论】:

      【解决方案3】:

      根据尝试在没有适当解析器的情况下解析和/或修改 XML 的系统的经验,让我说:不要这样做。使用 XML 解析器(这里有其他答案可以快速轻松地做到这一点)。

      使用非 xml 方法来解析和/或修改 XML 流总是会让您在未来的某个时候感到痛苦。我知道,因为我感受到了那种痛苦。

      我知道如果您使用正则表达式解决方案,它似乎会在运行时更快/更简单的代码/更容易理解/无论如何。但你以后只会让某人的生活变得悲惨。

      【讨论】:

      • 你说得很好,我认为这里提出的一些其他解决方案(XSLT、XPATH 或 XDocument)将使我在未来免于痛苦。
      • 很少有绝对规则没有例外。 “永远不要使用字符串操作工具来解析或修改 XML”就是其中之一。
      【解决方案4】:

      如果您对要匹配的内容足够了解,您可以使用正则表达式。例如,如果您正在寻找其中包含“密码”一词且没有内部标签的任何标签,则此正则表达式将起作用:

      (<([^>]*?password[^>]*?)>)([^<]*?)(<\/\2>)
      

      您也可以在 zowat 的回答中使用相同的 C# 替换语句,但对于替换字符串,您可能希望使用“$1XXXXX$4”。

      【讨论】:

      • 不,你不能(或不应该)因为正则表达式不知道层次结构,不能加载和解析命名或数字实体,更不用说外部文档类型,CDATA 有问题并且不能处理命名空间。请改用 XSLT。这样你只会打开一罐蠕虫。
      • 问题明确提到了正则表达式。为什么每个人都试图提供不可行的解决方案?
      【解决方案5】:

      正则表达式是错误的方法,我已经看到它在你最意想不到的时候出现了严重的错误。

      无论如何,XDocument 更有趣:

      XDocument doc = XDocument.Parse(@"
                  <user>
                      <userid>jsmith</userid>
                      <password>password</password>
                  </user>");
      
      doc.Element("user").Element("password").Value = "XXXX";
      
      // Temp namespace just for the purposes of the example -
      XDocument doc2 = XDocument.Parse(@"
                  <secinfo xmlns:ns='http://tempuru.org/users'>
                      <ns:userid>jsmith</ns:userid>
                      <ns:password>password</ns:password>
                  </secinfo>");
      
      doc2.Element("secinfo").Element("{http://tempuru.org/users}password").Value = "XXXXX";
      

      【讨论】:

        【解决方案6】:

        这是我在使用 XMLDocument 时想到的,它可能不像 XSLT 那样流畅,但应该足够通用以处理各种文档:

                    //input is a String with some valid XML
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(input);
                    XmlNodeList nodeList = doc.SelectNodes("//*");
        
                    foreach (XmlNode node in nodeList)
                    {
                        if (node.Name.ToUpper().Contains("PASSWORD"))
                        {
                            node.InnerText = "XXXX";
                        }
                        else if (node.Attributes.Count > 0)
                        {
                            foreach (XmlAttribute a in node.Attributes)
                            {
                                if (a.LocalName.ToUpper().Contains("PASSWORD"))
                                {
                                    a.InnerText = "XXXXX";
                                }
                            }
                        }    
                    }
        

        【讨论】:

        • 我认为您想将 LocalName 用于元素和属性。此外,如果您将其设为遍历 XML 树的递归函数,则不必从构建文档中所有元素的列表开始。
        【解决方案7】:

        XSLT 存在的主要原因是能够转换 XML 结构,这意味着 XSLT 是一种样式表,可以用来改变元素的顺序以及改变元素的内容。因此,这是一种典型的情况,强烈建议使用 XSLT 而不是 Andrew Hare 在前一篇文章中所说的解析。

        【讨论】:

          猜你喜欢
          • 2018-01-07
          • 2010-11-23
          • 2014-10-28
          • 2016-04-06
          • 1970-01-01
          • 1970-01-01
          • 2017-02-21
          • 1970-01-01
          • 2012-11-02
          相关资源
          最近更新 更多