Як видалити всі простори імен з XML за допомогою C #?


104

Я шукаю чисте, елегантне та розумне рішення для видалення просторів імен з усіх елементів XML? Як би це функціонувало, щоб це виглядало?

Визначений інтерфейс:

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

Зразок 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>

Після того, як ми зателефонуємо на 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 # на .NET 3.5 SP1.


@JohnSaunders: Ви праві. Але саме в цьому випадку я маю зробити певну системну інтеграцію. І це був єдиний варіант тоді.
Петро Стегнар

@PeterStegnar помилка - це звичайно хакер, який створив застарілі формати. Часто розробники неправильно використовують xml. Простори імен - перша божевільна важлива особливість, яку потрібно відкинути.
Гусдор

Відповіді:


103

Ну ось остаточна відповідь. Я використовував чудову ідею Джиммі (яка, на жаль, сама по собі не є повною) і повну функцію рекурсії, щоб правильно працювати.

На основі інтерфейсу:

string RemoveAllNamespaces(string xmlDocument);

Я представляю тут остаточне чисте і універсальне рішення C # для видалення просторів імен XML:

//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%, але я його не дуже перевіряв, щоб не охопити деякі особливі випадки ... Але це хороша база для початку.


8
Наскільки добре це працює з атрибутами, які мають простори імен? Насправді ваш код просто ігнорує атрибути.
Джон Сондерс

6
Я усвідомлюю, що простори імен можуть бути корисними в деяких програмах, але зовсім не в моїх; вони викликали величезне роздратування. Це рішення спрацювало на мене.
JYelton

@John Saunders - Так, я використав це рішення і зрозумів, що ти маєш рацію. Я публікую оновлене рішення як відповідь
Конрад Моравський,

6
Це рішення НЕ працює для мене, оскільки код видаляє всі атрибути, а також простори імен. Звичайно, може
змінитися

@KonradMorawski, на жаль, я не бачу тут вашої відповіді. :(
Рамі А.

63

Позначена найбільш корисна відповідь має два недоліки:

  • Він ігнорує атрибути
  • Він не працює з елементами "змішаного режиму"

Ось мій погляд на це:

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

Зразок коду тут .


На жаль, це не спрацювало для мене, повернуто те саме введене xml. :(
Рамі А.

@RamiA. чи можете ви опублікувати фрагмент коду, щоб ми побачили, у чому проблема?
Декстер Легаспі

Не працював і для мене. Напевно, оскільки ви копіюєте атрибути, ви просто копіюєте їх xmlnsтакож.
MarioDS

1
Це працює. Я використовував це ... однак я зрозумів, що це не ретельна очистка в тому сенсі, що він не видаляє фактичні атрибути xmlns, що визначає простір імен ... тому я оновив це, щоб зробити саме це ... і додав приклад суті .
Декстер Легаспі

Потрібно додати (from a in e.Attributes().DistinctBy(x => x.Name.LocalName)до справиlang=""ru-ru"" xml:lang=""ru-ru""
smg

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

3
Я думаю, ти можеш показати людям VB, що ти можеш мати XML-літерал в C #.
Роберт Харві

1
@Robert, це не літера XML. Це струна. Є велика різниця!
CoderDennis

Джіммі, ти близький, але ще не там. :) Я пишу остаточне рішення на основі вашої ідеї. Я опублікую його там.
Пітер Стегнар

ти маєш рацію :), поки ти на це, я пропоную свою версію виправлення.
Джиммі

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

Yo це чудово працює, не тільки це працює, це навіть не впливає на файл, який записує дані, також на Xelement, використовуючи метод DescendantAndself. дякую людино!
Шон

працює для мене. Також обробляє CDATA, які інші рішення втрачають по шляху.
Павло

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

1
Працює для кореневого елемента, але не для вкладених елементів; простори імен, схоже, просто перейменовуються, можливо, автоматично в XElement.ToString ().
Рамі А.

Саме те, що мені потрібно було, щоб видалити всі простори імен, але зберігати атрибути.
Бассі

10

Обов’язковий відповідь за допомогою 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>

+1 для "обов'язкових". :-) Мені все ще цікаво, чому видалення просторів імен було б розумним рішенням. Це, ймовірно, виходить з ладу та горить на <елемент ns: attr = "a" attr = "b" />.
Томалак

Ну звичайно, але кожна техніка видалення НС буде більшою чи меншою мірою. Щодо чи дійсності, я можу сказати вам, де мені це потрібно: імпорт стороннього XML, коли вони не можуть розібрати дійсний XSD, але наполягають на просторі імен. Практичність в кінцевому рахунку правила.
annakata

1
@annakata: Рішення простіше, ніж ти думаєш. Припиніть активувати. Відмовтеся від використання будь-якої техніки, яка не розуміє XML. Єдина причина, по якій ми все ще змушені використовувати таке сміття - це те, що люди все частіше говорять "так", коли їм потрібно сказати "ні" трохи частіше. Стандартам понад 10 років! Чому ще у нас все ще є програмне забезпечення, яке не розуміє просторів імен XML, за винятком того, що ми продовжуємо включати його?
Джон Сондерс

3
@John - ha, є ті речі, які слід зробити, і є ті речі, які, на думку керівництва, будуть зроблені. Все для найкращого в кращому з усіх можливих світів.
annakata

1
@Tomalak одним випадком використання може бути, якщо вам потрібно перетворити на JSON і декларації простору імен, що перешкоджають цьому процесу.
devlord

10

І це ідеальне рішення, яке також видалить елементи 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, "");

1
Я повинен прочитати це 2 години тому. Зробив майже такий самий регулярний вираз, єдине, що працювало у складному XML, з багатьма просторами імен, атрибутами тощо
TPAKTOPA

Майте на увазі, що вам все-таки доведеться очищати елементи, наприклад <xxx: tagname>. Я використав наступний код (відмова від відповідальності, працює на моїй машині): Regex.Replace(xmlStr, @"<(/?)([^>\s:]+):([^>]+)>", "<$1$3>")
Едвін,

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

1
Ваша відповідь є єдиною, яка не схожа на злом, однак оригінальний зразок у посиланні блогу, на який посилається, є правильнішим, оскільки якщо ви не видалите простір імен у кореневому вузлі, то всі дочірні вузли та атрибути без Простір імен успадкує кореневий простір імен.
тенор

8

Це рішення, засноване на прийнятій відповіді Петра Стегнара.

Я ним користувався, але (як зауважили andygjp та Джон Сондерс) його код ігнорує атрибути .

Мені потрібно було також подбати про атрибути, тому я адаптував його код. Версія Енді була 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;
    }

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

1
Дякую за це рішення! Підходить для мого питання.
AngieM

1
Таким чином, це було ідеальним, оскільки воно працювало над атрибутами. Вдалося це використати без проблем. Дякую
Джуліан гуппі

4

Це можна зробити за допомогою 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();
}

3

Трохи модифікована відповідь Петра, це буде добре працювати і для атрибута, включаючи видалення простору імен та префікса. Трохи шкода коду виглядає трохи некрасиво.

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

    }

+1 Це працювало для мене. Однак він залишає атрибути, які були частиною визначення простору імен (видалений лише префікс xmlns), але вони на даний момент не впливають на серіалізацію.
Рамі А.

2

Відповіді Джиммі та Пітера були чудовою підмогою, але вони фактично видалили всі атрибути, тому я вніс невелику модифікацію:

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

2

Трохи запізнився на вечірку на цьому, але ось що я нещодавно використав:

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

Здається, це не працює? Він знаходить усі декларації простору імен, але виклик Remove () у цій колекції нічого не робить. Я спробував result.ToString (), і отримана рядок все ще має атрибут xmlns. Я щось роблю не так?
Джиммі

Це працювало на те, що мені було потрібно в той час, але зараз я озираюся на це, це не 100%. Він видаляє префікси простору імен з вузлів (що мені потрібно), але ви маєте рацію щодо цього, залишаючи атрибут xmlns позаду. Дивно, що цей атрибут також не розпізнається жодним із методів XDocument!
MarcE

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

1

Ось моя версія 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

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

1

Не вдаючись до рішення, заснованого на XSLT, якщо ви хочете бути чистим, елегантним та розумним, вам знадобиться певна підтримка з боку, зокрема, шаблон відвідувачів може зробити це вітерцем. На жаль, тут це недоступно.

Я реалізував це натхненно LINQ, ExpressionVisitorщоб мати подібну структуру до нього. За допомогою цього ви можете застосувати шаблон відвідувача до (LINQ до) об’єктів 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);
    }
}

ps, ця конкретна реалізація використовує деякі функції .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);

Використовуючи цього відвідувача, ви можете написати a, 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;
        }
    }
}

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>

що справді здається притаманною проблемою. Але оскільки питання вказувало на виведення рядка, а не XElement, у цьому випадку у вас може бути рішення, яке виводило б дійсну рядок, що був недійсним XElement.

Мені також сподобалась відповідь jocull за допомогою користувальницького XmlWriter, але коли я спробував це, у мене це не вийшло. Хоча все виглядає правильно, я не міг сказати, чи має клас XmlNoNamespaceWriter взагалі якийсь ефект; це точно не було видалення просторів імен, як я цього хотів.


1

Додавання мого також очищає ім'я вузлів, що мають префікси простору імен:

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

1

Я спробував перші кілька рішень і не працював для мене. В основному проблема з видаленням атрибутів, як і інші, вже згадувалася. Я б сказав, що мій підхід дуже схожий на Джиммі, використовуючи конструктори 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);
}

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

1

Ось 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

Попередження: можуть бути крайні корпуси!


0

Відповідь користувача892217 майже правильна. Він не компілюється так, як є, тому потребує невеликої корекції рекурсивного виклику:

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

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

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

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

0

Я спробував деякі рішення, але, як стверджується багатьма, є деякі крайні випадки.

Використовував деякі вищевказані регекси, але дійшов висновку, що одноетапний регулярний вираз не є можливим.

Отож ось моє рішення, двоступеневий регулярний вираз, знайдіть теги, в межах тегів видаліть, не змінюйте дані:

            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% працює для мене.


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

-1

Я думаю, що це найкоротша відповідь (але для таких конструкцій, ви матимете ще одне обговорення, у мене також є регекс для перетворення "<bcm:info></bcm:info>"в " <info></info>", але це не було оптимізовано. Якщо хтось запитає, я поділюсь цим. Отже, моє рішення таке:

    public string RemoveAllNamespaces(string xmlDocument)
    {
        return Regex.Replace(xmlDocument, @"\sxmlns(\u003A\w+)?\u003D\u0022.+\u0022", " ");
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.