การละเว้น xsi และ xsd namespaces ทั้งหมดเมื่อทำให้วัตถุเป็นอนุกรมใน. NET?


134

รหัสมีลักษณะดังนี้:

StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings))
{
    XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
    s.Serialize(xmlWriter, objectToSerialize);
}

เอกสารที่ทำให้เป็นอนุกรมที่เป็นผลลัพธ์มีเนมสเปซดังนี้:

<message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
    xmlns="urn:something">
 ...
</message>

ในการลบเนมสเปซ xsi และ xsd ฉันสามารถทำตามคำตอบได้จากHow to serialize an object to XML without getting xmlns =” …”? .

ฉันต้องการแท็กข้อความเป็น<message>(ไม่มีแอตทริบิวต์เนมสเปซ) ฉันจะทำเช่นนี้ได้อย่างไร?


2
ฉันรู้ว่าคุณคิดว่านี่อาจทำให้ xml ของคุณดูดีขึ้น แต่การให้เนมสเปซและ xsd ที่สอดคล้องกันนั้นเป็นแนวทางปฏิบัติที่ดีกว่า

2
ฉันต้องการให้ xml ของฉันเป็น <message> เท่านั้นฉันกำลังพูดถึงการละเว้น xmlns: xsi และ xmlns: xsd namespaces
NetSide

5
สำหรับบันทึก: โดยทั่วไปนี่เป็นความผิดพลาดที่โง่เขลา เนมสเปซอยู่ที่นั่นด้วยเหตุผลและการลบออกทั้งหมดจะทำลายสิ่งต่างๆ สิ่งต่างๆเช่น deserialization
John Saunders

68
สังเกตว่าบางครั้งก็ไม่ใช่เรื่องโง่เขลาและไม่ใช่เรื่องผิด ตัวอย่างเช่นอาจต้องสร้างส่วนย่อยของเอกสารและนำมารวมกันในภายหลัง โดยส่วนตัวแล้วฉันต้องการสร้างเอกสารที่คล้ายกันและมีขนาดใหญ่มาก พวกมันทั้งหมดมีชิ้นส่วนขนาดใหญ่เหมือนกันอยู่ลึกเข้าไปในต้นไม้ ดังนั้นฉันจึงต้องสร้างชิ้นส่วนที่ไม่แปรเปลี่ยนไว้ก่อนและแทรกเป็นไบต์อาร์เรย์เมื่อสร้างเอกสาร ดังนั้นเพื่อให้เอาต์พุตอ่านได้ง่ายขึ้นและเล็กลงฉันจำเป็นต้องละเว้นการประกาศเนมสเปซบางส่วนในส่วนด้านในเนื่องจากมีอยู่ในระดับที่สูงขึ้น
Dmitry Tashkinov

คำตอบ:


235
...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);

2
ฉันแค่อยากจะเพิ่มว่าการลบเนมสเปซเริ่มต้นอาจส่งผลที่ไม่คาดคิดได้ตัวอย่างเช่นหากคุณใช้แอตทริบิวต์ XmlInclude เพื่อทำให้เป็นอนุกรมประเภทที่ได้รับเนมสเปซจะถูกเพิ่มให้กับแต่ละองค์ประกอบเหล่านี้ไม่ว่าคุณจะต้องการหรือไม่ก็ตามเนื่องจาก 'จำเป็นสำหรับ deserialization
Thomas Levesque

3
นอกจากนี้ยังไม่ได้ลบเนมสเปซ xml ทั้งหมดตามที่คำถามถาม จะลบเฉพาะเนมสเปซ xsi และ xsd ตามที่กล่าวไว้ในคำถามstackoverflow.com/questions/258960ซึ่งอ้างถึงในคำถามนี้ด้วย
Cheeso

1
MS ยังไม่รองรับตามที่กล่าวไว้ในคำตอบของฉันเอง มันไม่ได้เสมอไปโดยเฉพาะอย่างยิ่งเมื่อชนิดของคุณอาจจะใช้กับคนอื่น ๆ ที่ทำมี namespaces
fourpastmidnight

@ThomasLevesque วิธีการลบเนมสเปซเริ่มต้นขณะใช้แอตทริบิวต์ XmlInclude
Jeson Martajaya

4
สามารถย่อเป็นs.Serialize(writer, objectToSerialize, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Xeevis

28

นี่คือคำตอบที่ 2 จากสองคำตอบ

หากคุณต้องการเพียงแค่ตัดเนมสเปซทั้งหมดออกจากเอกสารโดยพลการระหว่างการทำให้เป็นอนุกรมคุณสามารถทำได้โดยใช้ XmlWriter ของคุณเอง

วิธีที่ง่ายที่สุดคือการได้มาจาก XmlTextWriter และแทนที่เมธอด StartElement ที่ปล่อยเนมสเปซ เมธอด StartElement ถูกเรียกใช้โดย XmlSerializer เมื่อปล่อยองค์ประกอบใด ๆ รวมทั้งรูท การลบล้างเนมสเปซสำหรับแต่ละองค์ประกอบและแทนที่ด้วยสตริงว่างหมายความว่าคุณได้ลอกเนมสเปซออกจากเอาต์พุต

public class NoNamespaceXmlWriter : XmlTextWriter
{
    //Provide as many contructors as you need
    public NoNamespaceXmlWriter(System.IO.TextWriter output)
        : base(output) { Formatting= System.Xml.Formatting.Indented;}

    public override void WriteStartDocument () { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

สมมติว่านี่คือประเภท:

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; } 
        get { return _Label; } 
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; } 
        get { return _Epoch; } 
    }
}

นี่คือวิธีที่คุณจะใช้สิ่งดังกล่าวในระหว่างการทำให้เป็นอนุกรม:

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.StringWriter(builder)))
        {
            s2.Serialize(writer, o2, ns2);
        }            
        Console.WriteLine("{0}",builder.ToString());

XmlTextWriter เป็นประเภทที่เสียแม้ว่า ตามเอกสารอ้างอิงเมื่อเขียนจะไม่ได้ตรวจสอบสิ่งต่อไปนี้:

  • อักขระไม่ถูกต้องในชื่อแอตทริบิวต์และองค์ประกอบ

  • อักขระ Unicode ที่ไม่พอดีกับการเข้ารหัสที่ระบุ หากอักขระ Unicode ไม่พอดีกับการเข้ารหัสที่ระบุ XmlTextWriter จะไม่หลบหนีอักขระ Unicode ไปเป็นเอนทิตีอักขระ

  • แอตทริบิวต์ที่ซ้ำกัน

  • อักขระในตัวระบุสาธารณะ DOCTYPE หรือตัวระบุระบบ

ปัญหาเหล่านี้กับ XmlTextWriter เกิดขึ้นตั้งแต่ v1.1 ของ. NET Framework และจะยังคงอยู่เพื่อความเข้ากันได้แบบย้อนหลัง หากคุณไม่มีความกังวลเกี่ยวกับปัญหาเหล่านั้นให้ใช้ XmlTextWriter แต่คนส่วนใหญ่ต้องการความน่าเชื่อถือมากขึ้นเล็กน้อย

เพื่อให้ได้สิ่งนั้นในขณะที่ยังคงระงับเนมสเปซระหว่างการทำให้เป็นอนุกรมแทนที่จะหามาจาก XmlTextWriter ให้กำหนดการใช้งานXmlWriterแบบนามธรรมและ 24 วิธีอย่าง เป็นรูปธรรม

ตัวอย่างอยู่ที่นี่:

public class XmlWriterWrapper : XmlWriter
{
    protected XmlWriter writer;

    public XmlWriterWrapper(XmlWriter baseWriter)
    {
        this.Writer = baseWriter;
    }

    public override void Close()
    {
        this.writer.Close();
    }

    protected override void Dispose(bool disposing)
    {
        ((IDisposable) this.writer).Dispose();
    }

    public override void Flush()
    {
        this.writer.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return this.writer.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        this.writer.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        this.writer.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        this.writer.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        this.writer.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        this.writer.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        this.writer.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        this.writer.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        this.writer.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        this.writer.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        this.writer.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        this.writer.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        this.writer.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        this.writer.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        this.writer.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        this.writer.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument()
    {
        this.writer.WriteStartDocument();
    }

    public override void WriteStartDocument(bool standalone)
    {
        this.writer.WriteStartDocument(standalone);
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        this.writer.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteString(string text)
    {
        this.writer.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        this.writer.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteValue(bool value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(DateTime value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(decimal value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(double value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(int value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(long value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(object value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(float value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(string value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteWhitespace(string ws)
    {
        this.writer.WriteWhitespace(ws);
    }


    public override XmlWriterSettings Settings
    {
        get
        {
            return this.writer.Settings;
        }
    }

    protected XmlWriter Writer
    {
        get
        {
            return this.writer;
        }
        set
        {
            this.writer = value;
        }
    }

    public override System.Xml.WriteState WriteState
    {
        get
        {
            return this.writer.WriteState;
        }
    }

    public override string XmlLang
    {
        get
        {
            return this.writer.XmlLang;
        }
    }

    public override System.Xml.XmlSpace XmlSpace
    {
        get
        {
            return this.writer.XmlSpace;
        }
    }        
}

จากนั้นจัดเตรียมคลาสที่ได้รับมาซึ่งแทนที่เมธอด StartElement เหมือนเดิม:

public class NamespaceSupressingXmlWriter : XmlWriterWrapper
{
    //Provide as many contructors as you need
    public NamespaceSupressingXmlWriter(System.IO.TextWriter output)
        : base(XmlWriter.Create(output)) { }

    public NamespaceSupressingXmlWriter(XmlWriter output)
        : base(XmlWriter.Create(output)) { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

จากนั้นใช้นักเขียนคนนี้ดังนี้:

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
        using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings))
            using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter))
            {
                s2.Serialize(writer, o2, ns2);
            }            
        Console.WriteLine("{0}",builder.ToString());

สินเชื่อเพื่อการนี้เพื่อOleg Tkachenko


3
ฉันพบว่าฉันจำเป็นต้องลบล้างLookupPrefix(string ns)เพื่อส่งคืนสตริงว่างเสมอเพื่อลบการประกาศสคีมาทั้งหมด
Kevin Brock

สิ่งนี้ไม่ตอบคำถามในทางเทคนิค - คุณกำลังใช้ XmlTextWriter ไม่ใช่ XmlWriter ฉันสังเกตเห็นเพราะฉันต้องการใช้ XmlWriter สำหรับ XmlWriterSettings ที่ฉันสามารถใช้กับมันได้
Abacus

@Abacus คุณอ่านรหัสหรือยัง? มันใช้XmlWriter และ XmlWriterSettings .
Cheeso

ฉันไม่ดีฉันต้องพลาดสิ่งนั้น
Abacus

คำตอบที่ดีนอกจากวิธีการเพิ่มจาก @KevinBrock แล้วฉันยังต้องโอเวอร์โหลด <! - language: lang-cs -> WriteStartAttribute (คำนำหน้าสตริง, สตริง localName, สตริง ns) ก่อนที่รหัสของฉันจะตัดออกทั้งหมด namespacing นอกจากนี้ควรสังเกตว่าคำนำหน้าเนมสเปซของฉันเปลี่ยนจาก b2p1 เป็น p2 ซึ่งทำให้ฉันตรวจสอบวิธีการอื่นโดยใช้คำนำหน้า
Mabdullah

15

หลังจากอ่านเอกสารของ Microsoft และวิธีแก้ปัญหาต่างๆทางออนไลน์ฉันได้ค้นพบวิธีแก้ปัญหานี้ ทำงานได้กับทั้งการXmlSerializerจัดลำดับ XML ในตัวและแบบกำหนดเองผ่านทางIXmlSerialiazble.

ฉันจะใช้MyTypeWithNamespacesตัวอย่าง XML เดียวกับที่ใช้ในคำตอบของคำถามนี้จนถึงตอนนี้

[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
    // As noted below, per Microsoft's documentation, if the class exposes a public
    // member of type XmlSerializerNamespaces decorated with the 
    // XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
    // namespaces during serialization.
    public MyTypeWithNamespaces( )
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            // Don't do this!! Microsoft's documentation explicitly says it's not supported.
            // It doesn't throw any exceptions, but in my testing, it didn't always work.

            // new XmlQualifiedName(string.Empty, string.Empty),  // And don't do this:
            // new XmlQualifiedName("", "")

            // DO THIS:
            new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
            // Add any other namespaces, with prefixes, here.
        });
    }

    // If you have other constructors, make sure to call the default constructor.
    public MyTypeWithNamespaces(string label, int epoch) : this( )
    {
        this._label = label;
        this._epoch = epoch;
    }

    // An element with a declared namespace different than the namespace
    // of the enclosing type.
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        get { return this._label; }
        set { this._label = value; }
    }
    private string _label;

    // An element whose tag will be the same name as the property name.
    // Also, this element will inherit the namespace of the enclosing type.
    public int Epoch
    {
        get { return this._epoch; }
        set { this._epoch = value; }
    }
    private int _epoch;

    // Per Microsoft's documentation, you can add some public member that
    // returns a XmlSerializerNamespaces object. They use a public field,
    // but that's sloppy. So I'll use a private backed-field with a public
    // getter property. Also, per the documentation, for this to work with
    // the XmlSerializer, decorate it with the XmlNamespaceDeclarations
    // attribute.
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
}

นั่นคือทั้งหมดสำหรับชั้นเรียนนี้ ตอนนี้บางคนคัดค้านที่จะมีXmlSerializerNamespacesวัตถุอยู่ในชั้นเรียนของตน แต่อย่างที่คุณเห็นฉันซ่อนมันไว้อย่างเรียบร้อยในตัวสร้างเริ่มต้นและเปิดเผยทรัพย์สินสาธารณะเพื่อส่งคืนเนมสเปซ

ตอนนี้เมื่อถึงเวลาที่ต้องทำให้คลาสเป็นลำดับคุณจะต้องใช้รหัสต่อไปนี้:

MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);

/******
   OK, I just figured I could do this to make the code shorter, so I commented out the
   below and replaced it with what follows:

// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
    new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");

******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();

// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.

// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;

// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);

เมื่อคุณทำเสร็จแล้วคุณจะได้ผลลัพธ์ดังต่อไปนี้:

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

ฉันใช้วิธีนี้สำเร็จแล้วในโปรเจ็กต์ล่าสุดที่มีคลาสแบบลึกที่ต่ออนุกรมกับ XML สำหรับการเรียกใช้บริการเว็บ เอกสารของ Microsoft ไม่ชัดเจนว่าจะทำอย่างไรกับXmlSerializerNamespacesสมาชิกที่เข้าถึงได้แบบสาธารณะเมื่อคุณสร้างขึ้นและหลายคนคิดว่ามันไม่มีประโยชน์ แต่ต่อไปนี้เอกสารของพวกเขาและใช้มันในลักษณะที่แสดงข้างต้นคุณสามารถกำหนดวิธีการ XmlSerializer สร้าง XML สำหรับชั้นเรียนของคุณโดยไม่ต้อง resorting พฤติกรรมที่ไม่สนับสนุนหรือ "กลิ้งของคุณเอง" IXmlSerializableเป็นอันดับโดยการใช้

ฉันหวังเป็นอย่างยิ่งว่าคำตอบนี้จะหยุดพักสักครั้งวิธีกำจัดมาตรฐานxsiและxsdเนมสเปซที่สร้างโดยไฟล์XmlSerializer.

UPDATE: ฉันแค่ต้องการให้แน่ใจว่าฉันตอบคำถามของ OP เกี่ยวกับการลบเนมสเปซทั้งหมด รหัสของฉันด้านบนจะใช้ได้กับสิ่งนี้ ให้ฉันแสดงวิธี ตอนนี้ในตัวอย่างด้านบนคุณไม่สามารถกำจัดเนมสเปซทั้งหมดได้ (เนื่องจากมีการใช้งานเนมสเปซสองชื่อ) ที่ไหนสักแห่งในเอกสาร XML ของคุณคุณจะต้องมีบางอย่างเช่นxmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. ถ้าชั้นในตัวอย่างที่เป็นส่วนหนึ่งของเอกสารที่มีขนาดใหญ่แล้วที่ไหนสักแห่งข้างต้น namespace ต้องได้รับการประกาศอย่างใดอย่างหนึ่งอย่างใดอย่างหนึ่ง (หรือทั้งสอง) และAbracadbra Whoohooถ้าไม่เช่นนั้นองค์ประกอบในเนมสเปซหนึ่งหรือทั้งสองจะต้องได้รับการตกแต่งด้วยคำนำหน้าของการเรียงลำดับ (คุณไม่สามารถมีเนมสเปซเริ่มต้นสองรายการได้ใช่ไหม) ดังนั้นสำหรับตัวอย่างนี้Abracadabraคือเนมสเปซที่ไม่มีค่า ฉันสามารถMyTypeWithNamespacesเพิ่มคำนำหน้าเนมสเปซภายในชั้นเรียนของฉันได้Whoohooดังนี้:

public MyTypeWithNamespaces
{
    this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
        new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
        new XmlQualifiedName("w", "urn:Whoohoo")
    });
}

ตอนนี้ในนิยามคลาสของฉันฉันระบุว่า<Label/>องค์ประกอบนั้นอยู่ในเนมสเปซ"urn:Whoohoo"ดังนั้นฉันจึงไม่จำเป็นต้องทำอะไรเพิ่มเติม เมื่อฉันจัดลำดับคลาสโดยใช้รหัสซีเรียลไลเซชันด้านบนของฉันไม่เปลี่ยนแปลงนี่คือผลลัพธ์:

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>

เนื่องจาก<Label>อยู่ในเนมสเปซที่แตกต่างจากส่วนที่เหลือของเอกสารจึงต้อง "ตกแต่ง" ด้วยเนมสเปซในบางครั้ง สังเกตว่ายังไม่มีxsiและxsdเนมสเปซ


"เอกสารของ Microsoft แจ้งอย่างชัดเจนว่าไม่รองรับ" สนใจแบ่งปันที่ไหน
Dave Van den Eynde

เดฟที่คุณโพสต์ในคำตอบของฉันเป็นคำถามที่คล้ายกันXmlSerializer: ลบ xsi ที่ไม่จำเป็นและ namespaces xsdเชื่อมโยงอยู่ที่นี่: XmlSerializerNamespaces ชั้น
fourpastmidnight

1
คุณยังคงส่งเนมสเปซไปยังเมธอด Serialize ฉันคิดว่าแนวคิดในการจัดหาสมาชิกสาธารณะคือคุณไม่ต้องทำอย่างนั้นเหรอ? ฉันไม่สามารถใช้งานได้แม้ว่าจะไม่ส่งต่อไปยังเมธอด Serialize และน่าเสียดายที่ฉันไม่สามารถเข้าถึงการเรียกใช้วิธีนั้นได้ ฉันสามารถตั้งค่าอินสแตนซ์ XmlSerializer ให้ใช้ได้เท่านั้น
ขยี้

ฉันพบว่ามันเป็นสิ่งXmlWriterที่มีอยู่ในXmlMediaTypeFormatterเนมสเปซ xsi และ xsd ในเอาต์พุตของฉันโดยไม่คำนึงถึง สิ่งนี้มีผลกับผู้ที่ใช้ค่าเริ่มต้นของ WebApi XmlMediaTypeFormatterเท่านั้น ฉันคัดลอกซอร์สโค้ดสำหรับมันและแก้ไขเพื่อส่งผ่านคุณสมบัติ Namespaces ของฉันไปยังเมธอด Serialize ตามที่จำเป็นเพื่อป้องกันไม่ให้XmlWriterเพิ่มค่าเริ่มต้นสองค่าโดยอัตโนมัติ ดูคำตอบนี้
ปิ๊ง

@crush คำตอบที่คุณเชื่อมโยงนั้นทำให้เข้าใจผิด - ไม่ผิด แต่การยืนยันไม่ถูกต้องทั้งหมด หากคุณดูข้อมูลโค้ดแรกในคำตอบของฉันคุณจะเห็นความคิดเห็นที่ระบุอย่างชัดเจนว่า XmlSerializer ทำงานอย่างไรเมื่อคุณเปิดเผยสมาชิกประเภทสาธารณะที่XmlSerializerNamespacesตกแต่งด้วยไฟล์XmlNamespacesDeclarationAttribute. สิ่งนี้นำมาจาก MSDN โดยตรงและโดยพื้นฐานแล้วใช้เนมสเปซที่ประกาศเหล่านั้นแทนค่าเริ่มต้นที่มีให้โดยไฟล์XmlSerializer.
fourpastmidnight

6

นี่เป็นคำตอบแรกจากสองข้อของฉันสำหรับคำถามนี้

หากคุณต้องการการควบคุมที่ดีกว่า namespaces - ตัวอย่างเช่นถ้าคุณต้องการที่จะละเว้นบางส่วนของพวกเขา แต่คนอื่นไม่ได้หรือถ้าคุณต้องการที่จะแทนที่หนึ่ง namespace อีกด้วยคุณสามารถทำได้โดยใช้XmlAttributeOverrides

สมมติว่าคุณมีคำจำกัดความประเภทนี้:

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; } 
        get { return _Label; } 
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; } 
        get { return _Epoch; } 
    }
}

และรหัสหลอกการทำให้เป็นอนุกรมนี้:

        var o2= new MyTypeWithNamespaces() { ..initializers...};
        ns.Add( "", "urn:Abracadabra" );
        XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces));
        s2.Serialize(System.Console.Out, o2, ns);

คุณจะได้รับ XML นี้:

<MyTypeWithNamespaces xmlns="urn:Abracadabra">
  <Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

โปรดสังเกตว่ามีเนมสเปซเริ่มต้นบนองค์ประกอบรูทและยังมีเนมสเปซที่แตกต่างกันในองค์ประกอบ "ป้ายกำกับ" เนมสเปซเหล่านี้กำหนดโดยแอตทริบิวต์การตกแต่งประเภทในโค้ดด้านบน

เฟรมเวิร์กการทำให้เป็นอนุกรม Xml ใน. NET มีความเป็นไปได้ที่จะลบล้างแอ็ตทริบิวต์ที่ตกแต่งโค้ดจริงอย่างชัดเจน คุณทำสิ่งนี้กับคลาส XmlAttributesOverrides และเพื่อน ๆ สมมติว่าฉันมีประเภทเดียวกันและฉันเรียงลำดับด้วยวิธีนี้:

        // instantiate the container for all attribute overrides
        XmlAttributeOverrides xOver = new XmlAttributeOverrides();

        // define a set of XML attributes to apply to the root element
        XmlAttributes xAttrs1 = new XmlAttributes();

        // define an XmlRoot element (as if [XmlRoot] had decorated the type)
        // The namespace in the attribute override is the empty string. 
        XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};

        // add that XmlRoot element to the container of attributes
        xAttrs1.XmlRoot= xRoot;

        // add that bunch of attributes to the container holding all overrides
        xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);

        // create another set of XML Attributes
        XmlAttributes xAttrs2 = new XmlAttributes();

        // define an XmlElement attribute, for a type of "String", with no namespace
        var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};

        // add that XmlElement attribute to the 2nd bunch of attributes
        xAttrs2.XmlElements.Add(xElt);

        // add that bunch of attributes to the container for the type, and
        // specifically apply that bunch to the "Label" property on the type.
        xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2);

        // instantiate a serializer with the overrides 
        XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver);

        // serialize
        s3.Serialize(System.Console.Out, o2, ns2);

ผลลัพธ์จะเป็นดังนี้

<MyTypeWithNamespaces>
  <Label>Cimsswybclaeqjh</Label>
  <Epoch>97</Epoch>
</MyTypeWithNamespaces>

คุณได้ลอกเนมสเปซ

คำถามเชิงตรรกะคือคุณสามารถดึงเนมสเปซทั้งหมดออกจากประเภทที่กำหนดเองในระหว่างการทำให้เป็นอนุกรมโดยไม่ต้องผ่านการลบล้างอย่างชัดเจนได้หรือไม่? คำตอบคือใช่และจะทำอย่างไรในการตอบกลับครั้งต่อไปของฉัน


6
XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType());
TextWriter xmlWriter = new StreamWriter(filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
sr.Serialize(xmlWriter, objectToSerialize, namespaces);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.