如何使用 C# 从 XML 中删除所有命名空间?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/987135/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:58:42  来源:igfitidea点击:

How to remove all namespaces from XML with C#?

c#xml

提问by Peter Stegnar

I am looking for the clean, elegant and smart solution to remove namespacees from all XML elements? How would function to do that look like?

我正在寻找从所有 XML 元素中删除命名空间的干净、优雅和智能的解决方案?功能如何做到这一点?

Defined interface:

定义的接口:

public interface IXMLUtils
{
        string RemoveAllNamespaces(string xmlDocument);
}

Sample XML to remove NS from:

从以下位置删除 NS 的示例 XML:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfInserts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <insert>
    <offer xmlns="http://schema.peters.com/doc_353/1/Types">0174587</offer>
    <type2 xmlns="http://schema.peters.com/doc_353/1/Types">014717</type2>
    <supplier xmlns="http://schema.peters.com/doc_353/1/Types">019172</supplier>
    <id_frame xmlns="http://schema.peters.com/doc_353/1/Types" />
    <type3 xmlns="http://schema.peters.com/doc_353/1/Types">
      <type2 />
      <main>false</main>
    </type3>
    <status xmlns="http://schema.peters.com/doc_353/1/Types">Some state</status>
  </insert>
</ArrayOfInserts>

After we call RemoveAllNamespaces(xmlWithLotOfNs), we should get:

在我们调用 RemoveAllNamespaces(xmlWithLotOfNs) 之后,我们应该得到:

  <?xml version="1.0" encoding="utf-16"?>
    <ArrayOfInserts>
      <insert>
        <offer >0174587</offer>
        <type2 >014717</type2>
        <supplier >019172</supplier>
        <id_frame  />
        <type3 >
          <type2 />
          <main>false</main>
        </type3>
        <status >Some state</status>
      </insert>
    </ArrayOfInserts>

Preffered language of solution is C# on .NET 3.5 SP1.

首选的解决方案语言是 .NET 3.5 SP1 上的 C#。

采纳答案by Peter Stegnar

Well, here is the final answer. I have used great Jimmy idea (which unfortunately is not complete itself) and complete recursion function to work properly.

好吧,这是最终答案。我使用了很棒的 Jimmy 想法(不幸的是它本身并不完整)和完整的递归函数来正常工作。

Based on interface:

基于接口:

string RemoveAllNamespaces(string xmlDocument);

I represent here final clean and universal C# solution for removing XML namespaces:

我在这里代表用于删除 XML 命名空间的最终干净和通用的 C# 解决方案:

//Implemented based on interface, not part of algorithm
public static string RemoveAllNamespaces(string xmlDocument)
{
    XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(xmlDocument));

    return xmlDocumentWithoutNs.ToString();
}

//Core recursion function
 private static XElement RemoveAllNamespaces(XElement xmlDocument)
    {
        if (!xmlDocument.HasElements)
        {
            XElement xElement = new XElement(xmlDocument.Name.LocalName);
            xElement.Value = xmlDocument.Value;

            foreach (XAttribute attribute in xmlDocument.Attributes())
                xElement.Add(attribute);

            return xElement;
        }
        return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));
    }

It's working 100%, but I have not tested it much so it may not cover some special cases... But it is good base to start.

它 100% 工作,但我没有对其进行太多测试,因此它可能无法涵盖某些特殊情况......但它是一个很好的开始基础。

回答by Jimmy

the obligatory answer using LINQ:

使用 LINQ 的强制性答案:

static XElement stripNS(XElement root) {
    return new XElement(
        root.Name.LocalName,
        root.HasElements ? 
            root.Elements().Select(el => stripNS(el)) :
            (object)root.Value
    );
}
static void Main() {
    var xml = XElement.Parse(@"<?xml version=""1.0"" encoding=""utf-16""?>
    <ArrayOfInserts xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
      <insert>
        <offer xmlns=""http://schema.peters.com/doc_353/1/Types"">0174587</offer>
        <type2 xmlns=""http://schema.peters.com/doc_353/1/Types"">014717</type2>
        <supplier xmlns=""http://schema.peters.com/doc_353/1/Types"">019172</supplier>
        <id_frame xmlns=""http://schema.peters.com/doc_353/1/Types"" />
        <type3 xmlns=""http://schema.peters.com/doc_353/1/Types"">
          <type2 />
          <main>false</main>
        </type3>
        <status xmlns=""http://schema.peters.com/doc_353/1/Types"">Some state</status>
      </insert>
    </ArrayOfInserts>");
    Console.WriteLine(stripNS(xml));
}

回答by annakata

The obligatory answer using XSLT:

使用 XSLT 的强制性答案:

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

  <xsl:template match="/|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

回答by andygjp

The reply's by Jimmy and Peter were a great help, but they actually removed all attributes, so I made a slight modification:

Jimmy 和 Peter 的回复很有帮助,但他们实际上删除了所有属性,所以我做了一点修改:

Imports System.Runtime.CompilerServices

Friend Module XElementExtensions

    <Extension()> _
    Public Function RemoveAllNamespaces(ByVal element As XElement) As XElement
        If element.HasElements Then
            Dim cleanElement = RemoveAllNamespaces(New XElement(element.Name.LocalName, element.Attributes))
            cleanElement.Add(element.Elements.Select(Function(el) RemoveAllNamespaces(el)))
            Return cleanElement
        Else
            Dim allAttributesExceptNamespaces = element.Attributes.Where(Function(attr) Not attr.IsNamespaceDeclaration)
            element.ReplaceAttributes(allAttributesExceptNamespaces)
            Return element
        End If

    End Function

End Module

回答by Florian Dürrbaum

Pick it up again, in C# - added line for copying the attributes:

再次拿起它,在 C# 中 - 添加了用于复制属性的行:

    static XElement stripNS(XElement root)
    {
        XElement res = new XElement(
            root.Name.LocalName,
            root.HasElements ?
                root.Elements().Select(el => stripNS(el)) :
                (object)root.Value
        );

        res.ReplaceAttributes(
            root.Attributes().Where(attr => (!attr.IsNamespaceDeclaration)));

        return res;
    }

回答by jocull

I know this question is supposedly solved, but I wasn't totally happy with the way it was implemented. I found another source over here on the MSDN blogs that has an overridden XmlTextWriterclass that strips out the namespaces. I tweaked it a bit to get some other things I wanted in such as pretty formatting and preserving the root element. Here is what I have in my project at the moment.

我知道这个问题应该已经解决了,但我对它的实施方式并不完全满意。我在 MSDN 博客上找到了另一个来源,它有一个覆盖XmlTextWriter命名空间的类。我稍微调整了一下以获得我想要的其他一些东西,例如漂亮的格式和保留根元素。这是我目前在我的项目中所拥有的。

http://blogs.msdn.com/b/kaevans/archive/2004/08/02/206432.aspx

http://blogs.msdn.com/b/kaevans/archive/2004/08/02/206432.aspx

Class

班级

/// <summary>
/// Modified XML writer that writes (almost) no namespaces out with pretty formatting
/// </summary>
/// <seealso cref="http://blogs.msdn.com/b/kaevans/archive/2004/08/02/206432.aspx"/>
public class XmlNoNamespaceWriter : XmlTextWriter
{
    private bool _SkipAttribute = false;
    private int _EncounteredNamespaceCount = 0;

    public XmlNoNamespaceWriter(TextWriter writer)
        : base(writer)
    {
        this.Formatting = System.Xml.Formatting.Indented;
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement(null, localName, null);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        //If the prefix or localname are "xmlns", don't write it.
        //HOWEVER... if the 1st element (root?) has a namespace we will write it.
        if ((prefix.CompareTo("xmlns") == 0
                || localName.CompareTo("xmlns") == 0)
            && _EncounteredNamespaceCount++ > 0)
        {
            _SkipAttribute = true;
        }
        else
        {
            base.WriteStartAttribute(null, localName, null);
        }
    }

    public override void WriteString(string text)
    {
        //If we are writing an attribute, the text for the xmlns
        //or xmlns:prefix declaration would occur here.  Skip
        //it if this is the case.
        if (!_SkipAttribute)
        {
            base.WriteString(text);
        }
    }

    public override void WriteEndAttribute()
    {
        //If we skipped the WriteStartAttribute call, we have to
        //skip the WriteEndAttribute call as well or else the XmlWriter
        //will have an invalid state.
        if (!_SkipAttribute)
        {
            base.WriteEndAttribute();
        }
        //reset the boolean for the next attribute.
        _SkipAttribute = false;
    }

    public override void WriteQualifiedName(string localName, string ns)
    {
        //Always write the qualified name using only the
        //localname.
        base.WriteQualifiedName(localName, null);
    }
}

Usage

用法

//Save the updated document using our modified (almost) no-namespace XML writer
using(StreamWriter sw = new StreamWriter(this.XmlDocumentPath))
using(XmlNoNamespaceWriter xw = new XmlNoNamespaceWriter(sw))
{
    //This variable is of type `XmlDocument`
    this.XmlDocumentRoot.Save(xw);
}

回答by Dexter Legaspi

The tagged most useful answer has two flaws:

标记为最有用的答案有两个缺陷:

  • It ignores attributes
  • It doesn't work with "mixed mode" elements
  • 它忽略属性
  • 它不适用于“混合模式”元素

Here is my take on this:

这是我的看法:

 public static XElement RemoveAllNamespaces(XElement e)
 {
    return new XElement(e.Name.LocalName,
      (from n in e.Nodes()
        select ((n is XElement) ? RemoveAllNamespaces(n as XElement) : n)),
          (e.HasAttributes) ? 
            (from a in e.Attributes() 
               where (!a.IsNamespaceDeclaration)  
               select new XAttribute(a.Name.LocalName, a.Value)) : null);
  }          

Sample code here.

示例代码在这里

回答by Konrad Morawski

This is a solution based on Peter Stegnar's accepted answer.

这是基于 Peter Stegnar 接受的答案的解决方案。

I used it, but (as andygjp and John Saunders remarked) his code ignores attributes.

我使用了它,但是(正如 andygjp 和 John Saunders 所说)他的代码忽略了 attributes

I needed to take care of attributes too, so I adapted his code. Andy's version was Visual Basic, this is still c#.

我也需要处理属性,所以我修改了他的代码。Andy 的版本是 Visual Basic,这仍然是 c#。

I know it's been a while, but perhaps it'll save somebody some time one day.

我知道这已经有一段时间了,但也许有一天它会为某人节省一些时间。

    private static XElement RemoveAllNamespaces(XElement xmlDocument)
    {
        XElement xmlDocumentWithoutNs = removeAllNamespaces(xmlDocument);
        return xmlDocumentWithoutNs;
    }

    private static XElement removeAllNamespaces(XElement xmlDocument)
    {
        var stripped = new XElement(xmlDocument.Name.LocalName);            
        foreach (var attribute in
                xmlDocument.Attributes().Where(
                attribute =>
                    !attribute.IsNamespaceDeclaration &&
                    String.IsNullOrEmpty(attribute.Name.NamespaceName)))
        {
            stripped.Add(new XAttribute(attribute.Name.LocalName, attribute.Value));
        }
        if (!xmlDocument.HasElements)
        {
            stripped.Value = xmlDocument.Value;
            return stripped;
        }
        stripped.Add(xmlDocument.Elements().Select(
            el =>
                RemoveAllNamespaces(el)));            
        return stripped;
    }

回答by David

And this is the perfect solution that will also remove XSI elements. (If you remove the xmlns and don't remove XSI, .Net shouts at you...)

这是一个完美的解决方案,它也将删除 XSI 元素。(如果您删除 xmlns 并且不删除 XSI,.Net 会对您大喊大叫...)

string xml = node.OuterXml;
//Regex below finds strings that start with xmlns, may or may not have :and some text, then continue with =
//and ", have a streach of text that does not contain quotes and end with ". similar, will happen to an attribute
// that starts with xsi.
string strXMLPattern = @"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";
xml = Regex.Replace(xml, strXMLPattern, "");

回答by HymanG

Slightly modified Peter's answer, this would works fine for the attribute as well, including remove the namespace and prefix. A bit sorry for the code looks a bit ugly.

稍微修改了彼得的答案,这也适用于该属性,包括删除命名空间和前缀。有点抱歉代码看起来有点难看。

 private static XElement RemoveAllNamespaces(XElement xmlDocument)
        {
            if (!xmlDocument.HasElements)
            {
                XElement xElement = new XElement(xmlDocument.Name.LocalName);
                xElement.Value = xmlDocument.Value;

                foreach (XAttribute attribute in xmlDocument.Attributes())
                {
                    xElement.Add(new XAttribute(attribute.Name.LocalName, attribute.Value));
                }

                return xElement;
            }

            else
            {
                XElement xElement = new XElement(xmlDocument.Name.LocalName,  xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));

                foreach (XAttribute attribute in xmlDocument.Attributes())
                {
                    xElement.Add(new XAttribute(attribute.Name.LocalName, attribute.Value));
                }

                return xElement;
            }

    }