我正在寻找干净,优雅和智能的解决方案来从所有XML元素中删除名称空间?如何做到这一点的功能?
定义的界面:
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>
解决方案的优先语言是.NET 3.5 SP1上的C#。
答案 0 :(得分:99)
嗯,这是最后的答案。我使用了很棒的Jimmy想法(遗憾的是它本身并不完整)和完整的递归功能才能正常工作。
基于界面:
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%工作,但我没有进行过多次测试,因此它可能无法涵盖一些特殊情况......但它是开始的良好基础。
答案 1 :(得分:60)
标记最有用的答案有两个缺陷:
以下是我对此的看法:
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);
}
示例代码here。
答案 2 :(得分:25)
使用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));
}
答案 3 :(得分:24)
这样就可以了: - )
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)));
}
答案 4 :(得分: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;
}
答案 5 :(得分: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>
答案 6 :(得分: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);
}
}
//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);
}
答案 7 :(得分: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, "");
答案 8 :(得分: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;
}
答案 9 :(得分: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));
}
“流利”方法允许我这样做:
var xml = File.ReadAllText(presentationFile);
var xDoc = XDocument.Parse(xml);
var xRoot = xDoc.Root.WithoutNamespaces();
答案 10 :(得分:3)
稍微修改了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;
}
}
答案 11 :(得分:3)
你可以使用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();
}
答案 12 :(得分:2)
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
答案 13 :(得分:1)
在这一方面迟到了派对,但这是我最近使用的内容:
var doc = XDocument.Parse(xmlString);
doc.Root.DescendantNodesAndSelf().OfType<XElement>().Attributes().Where(att => att.IsNamespaceDeclaration).Remove();
(取自此MSDN Thread)
编辑根据下面的评论,虽然这会从节点中删除命名空间前缀,但它实际上并未删除xmlns属性。为此,您还需要将每个节点的名称重置为其本地名称(例如,名称减去命名空间)
foreach (var node in doc.Root.DescendantNodesAndSelf().OfType<XElement>())
{
node.Name = node.Name.LocalName;
}
答案 14 :(得分:1)
考虑可能交错TEXT和ELEMENT节点的另一种解决方案,例如:
<parent>
text1
<child1/>
text2
<child2/>
</parent>
代码:
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;
}
}
}
答案 15 :(得分:1)
我的回答,基于字符串操作,
最简单的代码,
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;
}
答案 16 :(得分:1)
我尝试了前几个解决方案,并没有为我工作。主要是像其他人已经提到的那样删除属性的问题。我会说我的方法与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);
}
答案 17 :(得分:1)
添加my还会清除具有名称空间前缀的节点的名称:
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;
}
答案 18 :(得分:1)
简单的解决方案,实际上就地重新命名元素,而不是创建副本,并且可以很好地替换属性。
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>
这似乎是一个固有的问题。但是因为问题表明输出了一个String而不是一个XElement,在这种情况下你可以有一个解决方案来输出一个无效的XElement的有效字符串。
我也喜欢使用自定义XmlWriter的jocull答案,但是当我尝试它时,它对我不起作用。虽然看起来都是正确的,但我无法判断XmlNoNamespaceWriter类是否有任何影响;它绝对不是我想要的删除名称空间。
答案 19 :(得分:1)
如果你想要一个基于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);
}
}
p.s。,这个特殊的实现使用一些.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;
}
}
}
答案 20 :(得分:1)
这是我的Dexter Legaspi C#版本的VB.NET版本
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
答案 21 :(得分:1)
对于要工作的属性,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;
}
答案 22 :(得分:0)
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;
}
答案 23 :(得分:0)
这对我有用。
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();
答案 24 :(得分:0)
经过多次寻找这个问题的解决方案之后,这个特别的页面似乎有最多的牛肉......然而,没有什么比这更合适了,所以我采用了老式的方式,只是解析了我想要的东西。希望这有助于某人。 (注意:这也会删除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);
}
答案 25 :(得分:0)
这里是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
警告:可能存在边缘情况!
答案 26 :(得分:0)
无需重新创建整个节点层次结构:
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);
}
}
答案 27 :(得分:0)
我尝试了一些解决方案,但正如许多人所说,存在一些极端情况。
使用了上面的一些正则表达式,但得出的结论是单步正则表达式是不可行的。
这是我的解决方案,两步正则表达式,找到标签,删除标签内的内容,不要更改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%。
答案 28 :(得分:-1)
我认为这是最简短的答案(但是对于像你这样的建筑,你会有另一个讨论,我也有正则表达式将"<bcm:info></bcm:info>"
转换为&#34; <info></info>
&#34;但它不是&# 39; t优化,如果有人问我,我会分享它。所以,我的解决方案是:
public string RemoveAllNamespaces(string xmlDocument)
{
return Regex.Replace(xmlDocument, @"\sxmlns(\u003A\w+)?\u003D\u0022.+\u0022", " ");
}
答案 29 :(得分:-1)
以下是针对此问题的基于正则表达式的解决方案......
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;
}