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

Pet*_*nar 100 c# xml

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

定义的界面:

public interface IXMLUtils
{
        string RemoveAllNamespaces(string xmlDocument);
}
Run Code Online (Sandbox Code Playgroud)

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

<?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>
Run Code Online (Sandbox Code Playgroud)

在我们调用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>
Run Code Online (Sandbox Code Playgroud)

解决方案的优先语言是.NET 3.5 SP1上的C#.

Pet*_*nar 100

好吧,这是最后的答案.我使用了很棒的Jimmy想法(遗憾的是它本身并不完整)和完整的递归功能才能正常工作.

基于界面:

string RemoveAllNamespaces(string xmlDocument);
Run Code Online (Sandbox Code Playgroud)

我在这里代表用于删除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)));
    }
Run Code Online (Sandbox Code Playgroud)

它是100%工作,但我没有测试它,所以它可能不会涵盖一些特殊情况...但它是良好的基础开始.

  • 具有名称空间的属性的效果如何?实际上,您的代码完全忽略了属性. (8认同)
  • 我意识到命名空间在某些应用程序中可能很有用,但在我的应用程序中根本不是; 他们造成了巨大的烦恼.这个解决方案对我有用. (6认同)
  • 这个解决方案对我不起作用,因为代码删除了所有属性以及命名空间.当然,可能会进行一些更改以查看要删除的属性是名称空间还是属性 (6认同)

Dex*_*spi 62

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

  • 它忽略了属性
  • 它不适用于"混合模式"元素

以下是我对此的看法:

 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);
  }          
Run Code Online (Sandbox Code Playgroud)

这里有示例代码.

  • 没有使用"强制性"的Linq +1 :-) (9认同)

Jim*_*mmy 26

使用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));
}
Run Code Online (Sandbox Code Playgroud)

  • 我想你可以向VB人员展示你可以在C#中拥有XML文字. (3认同)
  • 这剥夺了所有属性,而不仅仅是命名空间.有关修复,请参阅[florian的答案](http://stackoverflow.com/a/4951707). (3认同)

小智 25

这将成功:-)

foreach (XElement XE in Xml.DescendantsAndSelf())
{
    // Stripping the namespace by setting the name of the element to it's localname only
    XE.Name = XE.Name.LocalName;
    // replacing all attributes with attributes that are not namespaces and their names are set to only the localname
    XE.ReplaceAttributes((from xattrib in XE.Attributes().Where(xa => !xa.IsNamespaceDeclaration) select new XAttribute(xattrib.Name.LocalName, xattrib.Value)));
}
Run Code Online (Sandbox Code Playgroud)


小智 16

再次拿起它,在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;
    }
Run Code Online (Sandbox Code Playgroud)


ann*_*ata 9

使用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>
Run Code Online (Sandbox Code Playgroud)

  • @John - 哈,还有那些应该做的事情,还有管理层认为会做的事情.在所有可能的世界中,一切都是最好的. (3认同)

joc*_*ull 9

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

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

/// <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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

//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);
}
Run Code Online (Sandbox Code Playgroud)


小智 9

这是一个完美的解决方案,也将删除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, "");
Run Code Online (Sandbox Code Playgroud)


Kon*_*ski 8

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

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

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

我知道它已经有一段时间了,但也许有一天会节省一些时间.

    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;
    }
Run Code Online (Sandbox Code Playgroud)


ras*_*asx 6

我真的很喜欢Dexter去那里的地方所以我把它翻译成了一个"流畅的"扩展方法:

/// <summary>
/// Returns the specified <see cref="XElement"/>
/// without namespace qualifiers on elements and attributes.
/// </summary>
/// <param name="element">The element</param>
public static XElement WithoutNamespaces(this XElement element)
{
    if (element == null) return null;

    #region delegates:

        Func<XNode, XNode> getChildNode = e => (e.NodeType == XmlNodeType.Element) ? (e as XElement).WithoutNamespaces() : e;

        Func<XElement, IEnumerable<XAttribute>> getAttributes = e => (e.HasAttributes) ?
            e.Attributes()
                .Where(a => !a.IsNamespaceDeclaration)
                .Select(a => new XAttribute(a.Name.LocalName, a.Value))
            :
            Enumerable.Empty<XAttribute>();

        #endregion

    return new XElement(element.Name.LocalName,
        element.Nodes().Select(getChildNode),
        getAttributes(element));
}
Run Code Online (Sandbox Code Playgroud)

"流利"的方法允许我这样做:

var xml = File.ReadAllText(presentationFile);
var xDoc = XDocument.Parse(xml);
var xRoot = xDoc.Root.WithoutNamespaces();
Run Code Online (Sandbox Code Playgroud)