จะลบเนมสเปซทั้งหมดออกจาก 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: คุณพูดถูก แต่ในกรณีนี้ฉันต้องทำการรวมระบบบางอย่าง และนี่เป็นทางเลือกเดียวในตอนนั้น
Peter Stegnar

@PeterStegnar ข้อผิดพลาดมักจะเป็นของแฮ็กเกอร์ที่สร้างรูปแบบเดิม บ่อยครั้งที่นักพัฒนาใช้ xml ในทางที่ผิดอย่างต่อเนื่อง เนมสเปซเป็นคุณสมบัติที่สำคัญอย่างแรกที่ถูกทิ้งไป
Gusdor

คำตอบ:


103

นี่คือคำตอบสุดท้าย ฉันได้ใช้ความคิดที่ยอดเยี่ยมของ Jimmy (ซึ่งน่าเสียดายที่ยังไม่สมบูรณ์) และฟังก์ชันการเรียกซ้ำที่สมบูรณ์เพื่อให้ทำงานได้อย่างถูกต้อง

ขึ้นอยู่กับอินเทอร์เฟซ:

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
มันทำงานกับแอตทริบิวต์ที่มีเนมสเปซได้ดีเพียงใด ในความเป็นจริงรหัสของคุณเพียงแค่ละเว้นแอตทริบิวต์ทั้งหมด
John Saunders

6
ฉันตระหนักดีว่าเนมสเปซอาจมีประโยชน์ในบางแอพพลิเคชั่น แต่ไม่ใช่เลย พวกเขาสร้างความรำคาญอย่างมาก วิธีนี้ใช้ได้ผลสำหรับฉัน
JYelton

@ John Saunders - ใช่ฉันใช้วิธีแก้ปัญหานั้นและฉันรู้ว่าคุณคิดถูก ฉันกำลังโพสต์โซลูชันที่อัปเดตเป็นคำตอบ
Konrad Morawski

6
วิธีนี้ไม่ได้ผลสำหรับฉันเนื่องจากรหัสจะลบแอตทริบิวต์ทั้งหมดรวมทั้งเนมสเปซ แน่นอนการเปลี่ยนแปลงบางอย่างอาจได้ผลเพื่อดูว่าแอตทริบิวต์ที่ถูกลบออกเป็นเนมสเปซหรือแอตทริบิวต์
bigfoot

@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. คุณสามารถโพสต์ข้อมูลโค้ดเพื่อให้เราทราบว่าปัญหาคืออะไร
Dexter Legaspi

ไม่ได้ผลสำหรับฉันเช่นกัน ฉันเดาว่าเนื่องจากคุณกำลังคัดลอกแอตทริบิวต์คุณก็แค่คัดลอกไปxmlnsด้วย
MarioDS

1
นี้ได้ผล ฉันได้ใช้มัน ... อย่างไรก็ตามฉันตระหนักว่ามันไม่ได้ทำความสะอาดอย่างละเอียดในแง่ที่ว่ามันไม่ได้ลบแอตทริบิวต์ xmlns จริงที่กำหนดเนมสเปซ ... ดังนั้นฉันจึงอัปเดตเพื่อทำเช่นนั้น ... และเพิ่มตัวอย่างส่วนสำคัญ .
Dexter Legaspi

ต้องเพิ่ม(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 # ได้
Robert Harvey

1
@ โรเบิร์ตนั่นไม่ใช่ตัวอักษร XML มันเป็นสตริง มีความแตกต่างอย่างมาก!
CoderDennis

จิมมี่คุณอยู่ใกล้ แต่ยังไม่ถึงที่นั่น :) ฉันกำลังเขียนคำตอบสุดท้ายตามความคิดของคุณ ฉันจะโพสต์ไว้ที่นั่น
Peter Stegnar

คุณพูดถูก :) ในขณะที่คุณกำลังทำอยู่ฉันขอเสนอการแก้ไขในเวอร์ชันของตัวเอง
Jimmy

3
สิ่งนี้จะตัดแอตทริบิวต์ทั้งหมดออกไม่ใช่เฉพาะเนมสเปซ ดูคำตอบของ florianสำหรับการแก้ไข
Brian

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

นี่ใช้งานได้ดีไม่เพียง แต่ใช้งานได้เท่านั้น แต่ยังไม่ส่งผลกระทบต่อไฟล์ที่เขียนข้อมูลด้วย Xelement โดยใช้วิธี DescendantAndself ขอบคุณมาก!
shawn

เหมาะกับฉัน นอกจากนี้ยังจัดการ 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 ()
รามีอ.

สิ่งที่ฉันต้องการในการลบเนมสเปซทั้งหมด แต่เก็บแอตทริบิวต์ไว้
Bassie

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 สำหรับ "บังคับ" :-) ฉันยังสงสัยว่าทำไมการลบเนมสเปซจึงเป็นการตัดสินใจที่ชาญฉลาด อาจเกิดปัญหาและไหม้ใน <element ns: attr = "a" attr = "b" />
Tomalak

แน่นอน แต่ทุกเทคนิคการถอด NS จะมากขึ้นหรือน้อยลง ในฐานะที่เป็นหรือความถูกต้องฉันสามารถบอกคุณได้ว่าฉันต้องการตรงไหน: การนำเข้า XML ของบุคคลที่สามซึ่งพวกเขาไม่สามารถจัดเรียง XSD ที่ถูกต้องได้ แต่ยืนยันในการกำหนดเนมสเปซ ในท้ายที่สุดกฎการปฏิบัติจริง
annakata

1
@annakata: วิธีแก้ปัญหานั้นง่ายกว่าที่คุณคิด หยุดการเปิดใช้งาน ปฏิเสธที่จะใช้เทคนิคใด ๆ ที่ไม่เข้าใจ XML เหตุผลเดียวที่เรายังคงถูกบังคับให้ใช้ขยะดังกล่าวเป็นเพราะผู้คนมักพูดว่า "ใช่" เมื่อต้องพูดว่า "ไม่" บ่อยขึ้น มาตรฐานเก่ากว่า 10 ปี! เหตุใดเราจึงยังมีซอฟต์แวร์ที่ไม่เข้าใจ XML Namespaces ยกเว้นว่าเรายังคงเปิดใช้งานซอฟต์แวร์นี้ต่อไป
John Saunders

3
@ จอห์น - ฮ่ามีสิ่งที่ควรทำและมีสิ่งเหล่านั้นที่ฝ่ายบริหารเห็นว่าจะต้องทำ ทั้งหมดคือสิ่งที่ดีที่สุดในโลกที่ดีที่สุด
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 ชั่วโมงที่แล้ว regex เกือบจะเหมือนกันสิ่งเดียวที่ใช้งานได้ใน XML ที่ซับซ้อนมีเนมสเปซคุณลักษณะและอื่น ๆ มากมาย
TPAKTOPA

โปรดทราบว่าคุณยังคงต้องล้างองค์ประกอบเช่น <xxx: tagname> ฉันใช้รหัสต่อไปนี้ (ข้อจำกัดความรับผิดชอบใช้ได้กับเครื่องของฉัน): Regex.Replace(xmlStr, @"<(/?)([^>\s:]+):([^>]+)>", "<$1$3>")
Edwin

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

นี่เป็นวิธีแก้ปัญหาตามคำตอบที่ยอมรับของ 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;
    }

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
ดังนั้นนี่จึงเหมาะอย่างยิ่งเพราะมันใช้งานได้กับคุณสมบัติ ก็สามารถใช้งานได้โดยไม่มีปัญหา ขอบคุณ
julian guppy

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

คำตอบของ 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;
            }

    }

+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

นี่คือ 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

คำเตือน: อาจมีกรณีขอบ!


2

สายไปหน่อยสำหรับงานปาร์ตี้นี้ แต่นี่คือสิ่งที่ฉันใช้เมื่อเร็ว ๆ นี้:

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

ดูเหมือนจะไม่ทำงาน? พบการประกาศเนมสเปซทั้งหมด แต่การเรียก Remove () บนคอลเล็กชันนั้นไม่ได้ทำอะไรเลย ฉันลองใช้ผลลัพธ์ ToString () และสตริงผลลัพธ์ยังคงมีแอตทริบิวต์ xmlns ฉันทำอะไรผิดหรือเปล่า?
Jimmy

มันใช้ได้กับสิ่งที่ฉันต้องการในตอนนั้น แต่ตอนนี้ฉันมองย้อนกลับไปว่ามันยังไม่ 100% มันลบคำนำหน้าเนมสเปซออกจากโหนด (ซึ่งเป็นสิ่งที่ฉันต้องการ) แต่คุณคิดถูกแล้วที่ทิ้งแอตทริบิวต์ xmlns ไว้ข้างหลัง น่าแปลกที่แอตทริบิวต์นั้นไม่รู้จักโดยวิธี XDocument ใด ๆ เช่นกัน!
MarcE

1

สำหรับแอตทริบิวต์ที่จะทำงานสำหรับลูปสำหรับการเพิ่มแอตทริบิวต์ควรไปหลังจากการเรียกซ้ำนอกจากนี้ยังต้องตรวจสอบว่า 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

นี่คือ 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

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เพื่อให้มีโครงสร้างที่คล้ายกัน ด้วยวิธีนี้คุณสามารถใช้รูปแบบผู้เยี่ยมชมกับวัตถุ XML (LINQ-to-) (ฉันได้ทำการทดสอบแบบ จำกัด ในเรื่องนี้ แต่ก็ใช้ได้ดีเท่าที่ฉันสามารถบอกได้)

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>

ซึ่งดูเหมือนเป็นปัญหาโดยธรรมชาติ แต่เนื่องจากคำถามระบุว่าการส่งออก String ไม่ใช่ XElement ในกรณีนี้คุณสามารถมีโซลูชันที่จะส่งออกสตริงที่ถูกต้องซึ่งเป็น XElement ที่ไม่ถูกต้อง

ฉันชอบคำตอบของ jocull โดยใช้ XmlWriter ที่กำหนดเอง แต่เมื่อฉันลองแล้วมันไม่ได้ผลสำหรับฉัน แม้ว่าทุกอย่างจะดูถูกต้อง แต่ฉันไม่สามารถบอกได้ว่าคลาส XmlNoNamespaceWriter มีผลหรือไม่ แน่นอนว่ามันไม่ใช่การลบเนมสเปซอย่างที่ฉันต้องการ


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

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

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


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

ฉันลองใช้วิธีแก้ปัญหาบางอย่างแล้ว แต่ตามที่ระบุไว้โดยหลายคนมีบางกรณีที่เป็นประโยชน์

ใช้ regex บางส่วนด้านบน แต่ได้ข้อสรุปว่า regex ขั้นตอนเดียวไม่สามารถทำได้

นี่คือวิธีแก้ปัญหาของฉัน regex 2 ขั้นตอนค้นหาแท็กภายในแท็กลบอย่าแก้ไข cdata:

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

ตอนนี้มันใช้งานได้ 100% สำหรับฉัน


-1

นี่คือวิธีแก้ปัญหาตาม regex สำหรับปัญหานี้ ...

    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

ฉันคิดว่านี่เป็นคำตอบที่สั้นที่สุด (แต่สำหรับการตั้งค่าเช่นคุณจะมีการอภิปรายอื่นฉันยังมี regex ที่จะแปลง"<bcm:info></bcm:info>"เป็น " <info></info>" แต่มันไม่ได้รับการปรับให้เหมาะสมหากมีคนถามฉันฉันจะแบ่งปันดังนั้นวิธีแก้ปัญหาของฉันคือ:

    public string RemoveAllNamespaces(string xmlDocument)
    {
        return Regex.Replace(xmlDocument, @"\sxmlns(\u003A\w+)?\u003D\u0022.+\u0022", " ");
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.