How to remove all namespaces from XML with C#?

前端 未结 30 2222
悲哀的现实
悲哀的现实 2020-11-22 13:30

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?

Defined interface:

相关标签:
30条回答
  • 2020-11-22 13:47

    Without resorting to an XSLT-based solution, if you want clean, elegant and smart, you'd need some support from the framework, in particular, the visitor pattern could make this a breeze. Unfortunately, it's not available here.

    I've implemented it inspired by LINQ's ExpressionVisitor to have a similar structure to it. With this, you can apply the visitor pattern to (LINQ-to-) XML objects. (I've done limited testing on this but it works well as far as I can tell)

    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., this particular implementation uses some .NET 4 features to make implementation a bit easier/cleaner (usage of dynamic and default arguments). It shouldn't be too dificult to make it .NET 3.5 compatible, perhaps even .NET 2.0 compatible.

    Then to implement the visitor, here's a generalized one that can change multiple namespaces (and the prefix used).

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

    And a little helper method to get the ball rolling:

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

    Then to remove a namespace, you could call it like so:

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

    Using this visitor, you can write a INamespaceMappingManager to remove all namespaces.

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:48

    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;
        }
    
    0 讨论(0)
  • 2020-11-22 13:49

    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;
                }
    
        }
    
    0 讨论(0)
  • 2020-11-22 13:50

    Without recreating whole node hierarchy:

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:51

    The obligatory answer using 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>
    
    0 讨论(0)
  • 2020-11-22 13:52

    That will do the trick :-)

    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)));
    }
    
    0 讨论(0)
提交回复
热议问题