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

我正在寻找干净、优雅和智能的解决方案,以删除所有 XML 元素的名称空间?如何做到这一点看起来像什么?

Defined interface:

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

要从中删除 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>

在调用 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>

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

190339 次浏览

使用 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));
}

使用 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>

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.

基于界面:

string RemoveAllNamespaces(string xmlDocument);

我在这里展示了去除 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)));
}

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

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

重新拾起它,在 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;
}

我知道这个问题应该已经解决了,但我对它的实施方式并不完全满意。我在 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);
}
}

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);
}

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

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

以下是我对此的看法:

 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 给你.

这个解决方案是基于 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;
}

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

稍微修改一下 Peter 的回答,这对属性也可以很好地工作,包括删除名称空间和前缀。有点抱歉的代码看起来有点丑陋。

 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;
}


}

要使 For 循环中的属性起作用,添加属性应该在递归之后进行,还需要检查 IsNamespaceDeclaration:

private static XElement RemoveAllNamespaces(XElement xmlDocument)
{
XElement xElement;


if (!xmlDocument.HasElements)
{
xElement = new XElement(xmlDocument.Name.LocalName) { Value = xmlDocument.Value };
}
else
{
xElement = new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(RemoveAllNamespaces));
}


foreach (var attribute in xmlDocument.Attributes())
{
if (!attribute.IsNamespaceDeclaration)
{
xElement.Add(attribute);
}
}


return xElement;
}

你可以使用 Linq:

public static string RemoveAllNamespaces(string xmlDocument)
{
var xml = XElement.Parse(xmlDocument);
xml.Descendants().Select(o => o.Name = o.Name.LocalName).ToArray();
return xml.ToString();
}

这样就行了: -)

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)));
}

Here is a regex based solution to this problem...

    private XmlDocument RemoveNS(XmlDocument doc)
{
var xml = doc.OuterXml;
var newxml = Regex.Replace(xml, @"xmlns[:xsi|:xsd]*="".*?""","");
var newdoc = new XmlDocument();
newdoc.LoadXml(newxml);
return newdoc;
}

下面是我的 VB.NET 版本的 Dexter Legaspi C # 版本

Shared Function RemoveAllNamespaces(ByVal e As XElement) As XElement
Return New XElement(e.Name.LocalName, New Object() {(From n In e.Nodes Select If(TypeOf n Is XElement, RemoveAllNamespaces(TryCast(n, XElement)), n)), If(e.HasAttributes, (From a In e.Attributes Select a), Nothing)})
End Function

另一种解决方案,考虑到可能交错的文本和 ELEMENT 节点,例如:

<parent>
text1
<child1/>
text2
<child2/>
</parent>

Code:

using System.Linq;


namespace System.Xml.Linq
{
public static class XElementTransformExtensions
{
public static XElement WithoutNamespaces(this XElement source)
{
return new XElement(source.Name.LocalName,
source.Attributes().Select(WithoutNamespaces),
source.Nodes().Select(WithoutNamespaces)
);
}


public static XAttribute WithoutNamespaces(this XAttribute source)
{
return !source.IsNamespaceDeclaration
? new XAttribute(source.Name.LocalName, source.Value)
: default(XAttribute);
}


public static XNode WithoutNamespaces(this XNode source)
{
return
source is XElement
? WithoutNamespaces((XElement)source)
: source;
}
}
}

User892217的回答几乎是正确的,它不会像原来那样编译,所以需要对递归调用稍作修正:

private static XElement RemoveAllNamespaces(XElement xmlDocument)
{
XElement xElement;


if (!xmlDocument.HasElements)
{
xElement = new XElement(xmlDocument.Name.LocalName) { Value = xmlDocument.Value };
}
else
{
xElement = new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(x => RemoveAllNamespaces(x)));
}


foreach (var attribute in xmlDocument.Attributes())
{
if (!attribute.IsNamespaceDeclaration)
{
xElement.Add(attribute);
}
}


return xElement;
}

I think this is shortest answer(but for constuctions like , you will have another discussion, I also have regex to convert "<bcm:info></bcm:info>" to "<info></info>" but it wasn't optimized, If someone ask me I will share it. So, my solution is:

    public string RemoveAllNamespaces(string xmlDocument)
{
return Regex.Replace(xmlDocument, @"\sxmlns(\u003A\w+)?\u003D\u0022.+\u0022", " ");
}

如果您想要简洁、优雅和智能,那么不需要使用基于 XSLT 的解决方案,就需要框架提供一些支持,特别是访问者模式可以使之变得轻而易举。不幸的是,这里没有。

我受到 LINQ 的 ExpressionVisitor的启发,实现了一个类似的结构。这样,您就可以将访问者模式应用于(LINQ-to -) XML 对象。(我已经做了有限的测试,但它工作得很好,因为我可以告诉)

public abstract class XObjectVisitor
{
public virtual XObject Visit(XObject node)
{
if (node != null)
return node.Accept(this);
return node;
}


public ReadOnlyCollection<XObject> Visit(IEnumerable<XObject> nodes)
{
return nodes.Select(node => Visit(node))
.Where(node => node != null)
.ToList()
.AsReadOnly();
}


public T VisitAndConvert<T>(T node) where T : XObject
{
if (node != null)
return Visit(node) as T;
return node;
}


public ReadOnlyCollection<T> VisitAndConvert<T>(IEnumerable<T> nodes) where T : XObject
{
return nodes.Select(node => VisitAndConvert(node))
.Where(node => node != null)
.ToList()
.AsReadOnly();
}


protected virtual XObject VisitAttribute(XAttribute node)
{
return node.Update(node.Name, node.Value);
}


protected virtual XObject VisitComment(XComment node)
{
return node.Update(node.Value);
}


protected virtual XObject VisitDocument(XDocument node)
{
return node.Update(
node.Declaration,
VisitAndConvert(node.Nodes())
);
}


protected virtual XObject VisitElement(XElement node)
{
return node.Update(
node.Name,
VisitAndConvert(node.Attributes()),
VisitAndConvert(node.Nodes())
);
}


protected virtual XObject VisitDocumentType(XDocumentType node)
{
return node.Update(
node.Name,
node.PublicId,
node.SystemId,
node.InternalSubset
);
}


protected virtual XObject VisitProcessingInstruction(XProcessingInstruction node)
{
return node.Update(
node.Target,
node.Data
);
}


protected virtual XObject VisitText(XText node)
{
return node.Update(node.Value);
}


protected virtual XObject VisitCData(XCData node)
{
return node.Update(node.Value);
}


#region Implementation details
internal InternalAccessor Accessor
{
get { return new InternalAccessor(this); }
}


internal class InternalAccessor
{
private XObjectVisitor visitor;
internal InternalAccessor(XObjectVisitor visitor) { this.visitor = visitor; }


internal XObject VisitAttribute(XAttribute node) { return visitor.VisitAttribute(node); }
internal XObject VisitComment(XComment node) { return visitor.VisitComment(node); }
internal XObject VisitDocument(XDocument node) { return visitor.VisitDocument(node); }
internal XObject VisitElement(XElement node) { return visitor.VisitElement(node); }
internal XObject VisitDocumentType(XDocumentType node) { return visitor.VisitDocumentType(node); }
internal XObject VisitProcessingInstruction(XProcessingInstruction node) { return visitor.VisitProcessingInstruction(node); }
internal XObject VisitText(XText node) { return visitor.VisitText(node); }
internal XObject VisitCData(XCData node) { return visitor.VisitCData(node); }
}
#endregion
}


public static class XObjectVisitorExtensions
{
#region XObject.Accept "instance" method
public static XObject Accept(this XObject node, XObjectVisitor visitor)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(visitor, "visitor");


// yay, easy dynamic dispatch
Acceptor acceptor = new Acceptor(node as dynamic);
return acceptor.Accept(visitor);
}
private class Acceptor
{
public Acceptor(XAttribute node) : this(v => v.Accessor.VisitAttribute(node)) { }
public Acceptor(XComment node) : this(v => v.Accessor.VisitComment(node)) { }
public Acceptor(XDocument node) : this(v => v.Accessor.VisitDocument(node)) { }
public Acceptor(XElement node) : this(v => v.Accessor.VisitElement(node)) { }
public Acceptor(XDocumentType node) : this(v => v.Accessor.VisitDocumentType(node)) { }
public Acceptor(XProcessingInstruction node) : this(v => v.Accessor.VisitProcessingInstruction(node)) { }
public Acceptor(XText node) : this(v => v.Accessor.VisitText(node)) { }
public Acceptor(XCData node) : this(v => v.Accessor.VisitCData(node)) { }


private Func<XObjectVisitor, XObject> accept;
private Acceptor(Func<XObjectVisitor, XObject> accept) { this.accept = accept; }


public XObject Accept(XObjectVisitor visitor) { return accept(visitor); }
}
#endregion


#region XObject.Update "instance" method
public static XObject Update(this XAttribute node, XName name, string value)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");
Validation.CheckArgumentNull(value, "value");


return new XAttribute(name, value);
}
public static XObject Update(this XComment node, string value = null)
{
Validation.CheckNullReference(node);


return new XComment(value);
}
public static XObject Update(this XDocument node, XDeclaration declaration = null, params object[] content)
{
Validation.CheckNullReference(node);


return new XDocument(declaration, content);
}
public static XObject Update(this XElement node, XName name, params object[] content)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");


return new XElement(name, content);
}
public static XObject Update(this XDocumentType node, string name, string publicId = null, string systemId = null, string internalSubset = null)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");


return new XDocumentType(name, publicId, systemId, internalSubset);
}
public static XObject Update(this XProcessingInstruction node, string target, string data)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(target, "target");
Validation.CheckArgumentNull(data, "data");


return new XProcessingInstruction(target, data);
}
public static XObject Update(this XText node, string value = null)
{
Validation.CheckNullReference(node);


return new XText(value);
}
public static XObject Update(this XCData node, string value = null)
{
Validation.CheckNullReference(node);


return new XCData(value);
}
#endregion
}


public static class Validation
{
public static void CheckNullReference<T>(T obj) where T : class
{
if (obj == null)
throw new NullReferenceException();
}


public static void CheckArgumentNull<T>(T obj, string paramName) where T : class
{
if (obj == null)
throw new ArgumentNullException(paramName);
}
}

另外,这个特殊的实现使用了一些。NET 4特性,使实现更容易/更干净(使用 dynamic和默认参数)。做起来应该不难。NET 3.5兼容,甚至可能。NET 2.0兼容。

然后,为了实现访问者,这里提供了一个通用的访问者,它可以更改多个名称空间(以及所使用的前缀)。

public class ChangeNamespaceVisitor : XObjectVisitor
{
private INamespaceMappingManager manager;
public ChangeNamespaceVisitor(INamespaceMappingManager manager)
{
Validation.CheckArgumentNull(manager, "manager");


this.manager = manager;
}


protected INamespaceMappingManager Manager { get { return manager; } }


private XName ChangeNamespace(XName name)
{
var mapping = Manager.GetMapping(name.Namespace);
return mapping.ChangeNamespace(name);
}


private XObject ChangeNamespaceDeclaration(XAttribute node)
{
var mapping = Manager.GetMapping(node.Value);
return mapping.ChangeNamespaceDeclaration(node);
}


protected override XObject VisitAttribute(XAttribute node)
{
if (node.IsNamespaceDeclaration)
return ChangeNamespaceDeclaration(node);
return node.Update(ChangeNamespace(node.Name), node.Value);
}


protected override XObject VisitElement(XElement node)
{
return node.Update(
ChangeNamespace(node.Name),
VisitAndConvert(node.Attributes()),
VisitAndConvert(node.Nodes())
);
}
}


// and all the gory implementation details
public class NamespaceMappingManager : INamespaceMappingManager
{
private Dictionary<XNamespace, INamespaceMapping> namespaces = new Dictionary<XNamespace, INamespaceMapping>();


public NamespaceMappingManager Add(XNamespace fromNs, XNamespace toNs, string toPrefix = null)
{
var item = new NamespaceMapping(fromNs, toNs, toPrefix);
namespaces.Add(item.FromNs, item);
return this;
}


public INamespaceMapping GetMapping(XNamespace fromNs)
{
INamespaceMapping mapping;
if (!namespaces.TryGetValue(fromNs, out mapping))
mapping = new NullMapping();
return mapping;
}


private class NullMapping : INamespaceMapping
{
public XName ChangeNamespace(XName name)
{
return name;
}


public XObject ChangeNamespaceDeclaration(XAttribute node)
{
return node.Update(node.Name, node.Value);
}
}


private class NamespaceMapping : INamespaceMapping
{
private XNamespace fromNs;
private XNamespace toNs;
private string toPrefix;
public NamespaceMapping(XNamespace fromNs, XNamespace toNs, string toPrefix = null)
{
this.fromNs = fromNs ?? "";
this.toNs = toNs ?? "";
this.toPrefix = toPrefix;
}


public XNamespace FromNs { get { return fromNs; } }
public XNamespace ToNs { get { return toNs; } }
public string ToPrefix { get { return toPrefix; } }


public XName ChangeNamespace(XName name)
{
return name.Namespace == fromNs
? toNs + name.LocalName
: name;
}


public XObject ChangeNamespaceDeclaration(XAttribute node)
{
if (node.Value == fromNs.NamespaceName)
{
if (toNs == XNamespace.None)
return null;
var xmlns = !String.IsNullOrWhiteSpace(toPrefix)
? (XNamespace.Xmlns + toPrefix)
: node.Name;
return node.Update(xmlns, toNs.NamespaceName);
}
return node.Update(node.Name, node.Value);
}
}
}


public interface INamespaceMappingManager
{
INamespaceMapping GetMapping(XNamespace fromNs);
}


public interface INamespaceMapping
{
XName ChangeNamespace(XName name);
XObject ChangeNamespaceDeclaration(XAttribute node);
}

还有一个小小的帮助方法来让事情进行下去:

T ChangeNamespace<T>(T node, XNamespace fromNs, XNamespace toNs, string toPrefix = null) where T : XObject
{
return node.Accept(
new ChangeNamespaceVisitor(
new NamespaceMappingManager()
.Add(fromNs, toNs, toPrefix)
)
) as T;
}

然后,要删除一个名称空间,可以这样调用它:

var doc = ChangeNamespace(XDocument.Load(pathToXml),
fromNs: "http://schema.peters.com/doc_353/1/Types",
toNs: null);

使用这个访问器,您可以编写一个 INamespaceMappingManager来删除所有名称空间。

T RemoveAllNamespaces<T>(T node) where T : XObject
{
return node.Accept(
new ChangeNamespaceVisitor(new RemoveNamespaceMappingManager())
) as T;
}


public class RemoveNamespaceMappingManager : INamespaceMappingManager
{
public INamespaceMapping GetMapping(XNamespace fromNs)
{
return new RemoveNamespaceMapping();
}


private class RemoveNamespaceMapping : INamespaceMapping
{
public XName ChangeNamespace(XName name)
{
return name.LocalName;
}


public XObject ChangeNamespaceDeclaration(XAttribute node)
{
return null;
}
}
}

一个简单的解决方案,实际上就地重命名元素,而不是创建副本,并且可以很好地替换属性。

public void RemoveAllNamespaces(ref XElement value)
{
List<XAttribute> attributesToRemove = new List<XAttribute>();
foreach (void e_loopVariable in value.DescendantsAndSelf) {
e = e_loopVariable;
if (e.Name.Namespace != XNamespace.None) {
e.Name = e.Name.LocalName;
}
foreach (void a_loopVariable in e.Attributes) {
a = a_loopVariable;
if (a.IsNamespaceDeclaration) {
//do not keep it at all
attributesToRemove.Add(a);
} else if (a.Name.Namespace != XNamespace.None) {
e.SetAttributeValue(a.Name.LocalName, a.Value);
attributesToRemove.Add(a);
}
}
}
foreach (void a_loopVariable in attributesToRemove) {
a = a_loopVariable;
a.Remove();
}
}

注意: 这并不总是保留原始属性顺序,但是我相信如果它对您很重要,您可以很容易地改变它。

还要注意的是,如果 XElement 属性只对名称空间有唯一性,那么 可以也会抛出异常,比如:

<root xmlns:ns1="a" xmlns:ns2="b">
<elem ns1:dupAttrib="" ns2:dupAttrib="" />
</root>

which really seems like an inherent problem. But since the question indicated outputing a String, not an XElement, in this case you could have a solution that would output a valid String that was an invalid XElement.

我也喜欢使用自定义 XmlWriter 得到 jocull 的答案,但是当我试用它时,它对我不起作用。尽管所有的 看起来都是正确的,但我不能确定 XmlNoNamespaceWriter 类是否有任何效果; 它肯定没有按照我想要的方式删除名称空间。

添加 my that 还会清除具有名称空间前缀的节点的名称:

    public static string RemoveAllNamespaces(XElement element)
{
string tex = element.ToString();
var nsitems = element.DescendantsAndSelf().Select(n => n.ToString().Split(' ', '>')[0].Split('<')[1]).Where(n => n.Contains(":")).DistinctBy(n => n).ToArray();


//Namespace prefix on nodes: <a:nodename/>
tex = nsitems.Aggregate(tex, (current, nsnode) => current.Replace("<"+nsnode + "", "<" + nsnode.Split(':')[1] + ""));
tex = nsitems.Aggregate(tex, (current, nsnode) => current.Replace("</" + nsnode + "", "</" + nsnode.Split(':')[1] + ""));


//Namespace attribs
var items = element.DescendantsAndSelf().SelectMany(d => d.Attributes().Where(a => a.IsNamespaceDeclaration || a.ToString().Contains(":"))).DistinctBy(o => o.Value);
tex = items.Aggregate(tex, (current, xAttribute) => current.Replace(xAttribute.ToString(), ""));


return tex;
}

我尝试了前几种解决方案,但都不管用。主要的问题是像前面提到的那样删除属性。我想说的是,我的方法非常类似于 Jimmy,它使用 XElement 构造函数,将对象作为参数。

public static XElement RemoveAllNamespaces(this XElement element)
{
return new XElement(element.Name.LocalName,
element.HasAttributes ? element.Attributes().Select(a => new XAttribute(a.Name.LocalName, a.Value)) : null,
element.HasElements ? element.Elements().Select(e => RemoveAllNamespaces(e)) : null,
element.Value);
}

我的回答是,基于字符串操纵,
最简单的代码,

public static string hilangkanNamespace(string instrXML)
{
char chrOpeningTag = '<';
char chrClosingTag = '>';
char chrSpasi = ' ';
int intStartIndex = 0;
do
{
int intIndexKu = instrXML.IndexOf(chrOpeningTag, intStartIndex);
if (intIndexKu < 0)
break; //kalau dah ga ketemu keluar
int intStart = instrXML.IndexOfAny(new char[] { chrSpasi, chrClosingTag }, intIndexKu + 1); //mana yang ketemu duluan
if (intStart < 0)
break; //kalau dah ga ketemu keluar
int intStop = instrXML.IndexOf(chrClosingTag, intStart);
if (intStop < 0)
break; //kalau dah ga ketemu keluar
else
intStop--; //exclude si closingTag
int intLengthToStrip = intStop - intStart + 1;
instrXML = instrXML.Remove(intStart, intLengthToStrip);
intStartIndex = intStart;
} while (true);


return instrXML;
}

我真的很喜欢 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));
}

“流利”的方法让我可以这样做:

var xml = File.ReadAllText(presentationFile);
var xDoc = XDocument.Parse(xml);
var xRoot = xDoc.Root.WithoutNamespaces();

这招对我很管用。

       FileStream fs = new FileStream(filePath, FileMode.Open);


StreamReader sr = new StreamReader(fs);


DataSet ds = new DataSet();
ds.ReadXml(sr);
ds.Namespace = "";


string outXML = ds.GetXml();
ds.Dispose();
sr.Dispose();
fs.Dispose();

在为这个问题寻找了很多解决方案之后,这个特别的页面似乎有最多的牛肉... ... 然而,没有什么是完全合适的,所以我采用了老式的方法,只是解析了我想要删除的东西。希望这对谁有帮助。(注意: 这也会删除 SOAP 或类似的信封内容。)

        public static string RemoveNamespaces(string psXml)
{
//
// parse through the passed XML, and remove any and all namespace references...also
// removes soap envelope/header(s)/body, or any other references via ":" entities,
// leaving all data intact
//
string xsXml = "", xsCurrQtChr = "";
int xiPos = 0, xiLastPos = psXml.Length - 1;
bool xbInNode = false;


while (xiPos <= xiLastPos)
{
string xsCurrChr = psXml.Substring(xiPos, 1);
xiPos++;
if (xbInNode)
{
if (xsCurrChr == ":")
{
// soap envelope or body (or some such)
// we'll strip these node wrappers completely
// need to first strip the beginning of it off  (i.e. "<soap" or "<s")
int xi = xsXml.Length;
string xsChr = "";
do
{
xi--;
xsChr = xsXml.Substring(xi, 1);
xsXml = xsXml.Substring(0, xi);
} while (xsChr != "<");


// next, find end of node
string xsQt = "";
do
{
xiPos++;
if (xiPos <= xiLastPos)
{
xsChr = psXml.Substring(xiPos, 1);
if (xsQt.Length == 0)
{
if (xsChr == "'" || xsChr == "\"")
{
xsQt = xsChr;
}
}
else
{
if (xsChr == xsQt)
{
xsQt = "";  // end of quote
}
else
{
if (xsChr == ">") xsChr = "x";      // stay in loop...this is not end of node
}
}
}
} while (xsChr != ">" && xiPos <= xiLastPos);
xiPos++;            // skip over closing ">"
xbInNode = false;
}
else
{
if (xsCurrChr == ">")
{
xbInNode = false;
xsXml += xsCurrChr;
}
else
{
if (xsCurrChr == " " || xsCurrChr == "\t")
{
// potential namespace...let's check...next character must be "/"
// or more white space, and if not, skip until we find such
string xsChr = "";
int xiOrgLen = xsXml.Length;
xsXml += xsCurrChr;
do
{
if (xiPos <= xiLastPos)
{
xsChr = psXml.Substring(xiPos, 1);
xiPos++;
if (xsChr == " " || xsChr == "\r" || xsChr == "\n" || xsChr == "\t")
{
// carry on..white space
xsXml += xsChr;
}
else
{
if (xsChr == "/" || xsChr == ">")
{
xsXml += xsChr;
}
else
{
// namespace! - get rid of it
xsXml = xsXml.Substring(0, xiOrgLen - 0);       // first, truncate any added whitespace
// next, peek forward until we find "/" or ">"
string xsQt = "";
do
{
if (xiPos <= xiLastPos)
{
xsChr = psXml.Substring(xiPos, 1);
xiPos++;
if (xsQt.Length > 0)
{
if (xsChr == xsQt) xsQt = ""; else xsChr = "x";
}
else
{
if (xsChr == "'" || xsChr == "\"") xsQt = xsChr;
}
}
} while (xsChr != ">" && xsChr != "/" && xiPos <= xiLastPos);
if (xsChr == ">" || xsChr == "/") xsXml += xsChr;
xbInNode = false;
}
}
}
} while (xsChr != ">" && xsChr != "/" && xiPos <= xiLastPos);
}
else
{
xsXml += xsCurrChr;
}
}
}
}
else
{
//
// if not currently inside a node, then we are in a value (or about to enter a new node)
//
xsXml += xsCurrChr;
if (xsCurrQtChr.Length == 0)
{
if (xsCurrChr == "<")
{
xbInNode = true;
}
}
else
{
//
// currently inside a quoted string
//
if (xsCurrQtChr == xsCurrChr)
{
// finishing quoted string
xsCurrQtChr = "";
}
}
}
}


return (xsXml);
}

下面是正则表达式(Regex)替换一行程序:

public static string RemoveNamespaces(this string xml)
{
return Regex.Replace(xml, "((?<=<|<\\/)|(?<= ))[A-Za-z0-9]+:| xmlns(:[A-Za-z0-9]+)?=\".*?\"", "");
}

下面是一个例子: https://regex101.com/r/fopydN/6

警告: 可能存在边缘情况!

不需要重建整个节点层次结构:

private static void RemoveDefNamespace(XElement element)
{
var defNamespase = element.Attribute("xmlns");
if (defNamespase != null)
defNamespase.Remove();


element.Name = element.Name.LocalName;
foreach (var child in element.Elements())
{
RemoveDefNamespace(child);
}
}

现在说这个有点晚了,不过我最近用了这个:

var doc = XDocument.Parse(xmlString);
doc.Root.DescendantNodesAndSelf().OfType<XElement>().Attributes().Where(att => att.IsNamespaceDeclaration).Remove();

(摘自此 MSDN 线程)

编辑 根据下面的注释,似乎这样做从节点中删除了名称空间前缀,但实际上并没有删除 xmlns 属性。为此,您还需要将每个节点的名称重置为其本地名称(例如名称减去名称空间)

foreach (var node in doc.Root.DescendantNodesAndSelf().OfType<XElement>())
{
node.Name = node.Name.LocalName;
}

我尝试了一些解决方案,但正如许多人所说的,存在一些边缘情况。

Used some of the regexes above, but came to the conclusion that a one step regex is not feasable.

所以这里是我的解决方案,2步正则表达式,查找标签,在标签移除,不改变 cdata:

            Func<Match, String> NamespaceRemover = delegate (Match match)
{
var result = match.Value;
if (String.IsNullOrEmpty(match.Groups["cdata"].Value))
{
// find all prefixes within start-, end tag and attributes and also namespace declarations
return Regex.Replace(result, "((?<=<|<\\/| ))\\w+:| xmlns(:\\w+)?=\".*?\"", "");
}
else
{
// cdata as is
return result;
}
};
// XmlDocument doc;
// string file;
doc.LoadXml(
Regex.Replace(File.ReadAllText(file),
// find all begin, cdata and end tags (do not change order)
@"<(?:\w+:?\w+.*?|(?<cdata>!\[CDATA\[.*?\]\])|\/\w+:?\w+)>",
new MatchEvaluator(NamespaceRemover)
)
);

现在它是100% 为我工作。

Peter Stegnar 代码的 vb.net 版本。

Public Class XElementRemoveNameSpace
'
'based on
'  Peter Stegnar's code here
'   https://stackoverflow.com/questions/987135/how-to-remove-all-namespaces-from-xml-with-c
'
Public Shared Function RemoveAllNamespaces(inXML As XElement) As XElement
Dim Xel As XElement = _RemoveAllNamespaces(inXML)
Return Xel
End Function


Private Shared Function _RemoveAllNamespaces(inXML As XElement) As XElement
Dim Xel As XElement
If Not inXML.HasElements Then
Xel = New XElement(inXML.Name.LocalName)
Xel.Value = inXML.Value
SetAttrs(inXML, Xel)
Return Xel
Else
Xel = New XElement(inXML.Name.LocalName, inXML.Elements().Select(Function(el) _RemoveAllNamespaces(el)))
SetAttrs(inXML, Xel) 'in case there are attributes on this element
Return Xel
End If
End Function


Private Shared Sub SetAttrs(inXML As XElement, outXML As XElement)
If inXML.HasAttributes Then
Dim nm As String
Dim NamespaceName As String
For Each attribute As XAttribute In inXML.Attributes()
Try
nm = attribute.Name.LocalName
' NamespaceName = attribute.Name.NamespaceName 'for debug
outXML.SetAttributeValue(nm, attribute.Value) 'stop invOP exception
Catch invOP As InvalidOperationException
'todo dup attributes - should not happen
Catch ex As Exception
End Try
Next
End If
End Sub
End Class