هل تريد حذف كافة مساحات الأسماء xsi وxsd عند إجراء تسلسل لكائن في .NET؟

StackOverflow https://stackoverflow.com/questions/625927

  •  05-07-2019
  •  | 
  •  

سؤال

يبدو الرمز كما يلي:

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، يمكنني متابعة الإجابة من كيفية إجراء تسلسل لكائن إلى XML دون الحصول على xmlns=”…”؟.

أريد علامة رسالتي باسم <message> (بدون أي سمات مساحة الاسم).كيف يمكنني أن أفعل هذا؟

هل كانت مفيدة؟

المحلول

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

نصائح أخرى

وهذا هو الثاني من إجابتين.

إذا كنت تريد فقط تجريد كافة مساحات الأسماء بشكل تعسفي من مستند أثناء التسلسل، فيمكنك القيام بذلك عن طريق تطبيق 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 موجودة منذ الإصدار 1.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());

الائتمان لهذا ل أوليغ تكاتشينكو.

بعد قراءة وثائق 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 لفئاتك دون اللجوء إلى سلوك غير مدعوم أو "إجراء تسلسل خاص بك" من خلال تنفيذ IXmlSerializable.

آمل أن تضع هذه الإجابة حدًا نهائيًا لكيفية التخلص من المعيار xsi و xsd مساحات الأسماء التي تم إنشاؤها بواسطة XmlSerializer.

تحديث:أريد فقط التأكد من أنني أجبت على سؤال OP حول إزالة جميع مساحات الأسماء.سيعمل الكود الخاص بي أعلاه على هذا؛دعني اريك كيف.الآن، في المثال أعلاه، لا يمكنك حقًا التخلص من جميع مساحات الأسماء (نظرًا لوجود مساحتي أسماء قيد الاستخدام).في مكان ما في مستند XML الخاص بك، ستحتاج إلى شيء مثل xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo.إذا كانت الفئة في المثال جزءًا من مستند أكبر، فيجب الإعلان في مكان ما فوق مساحة الاسم لأي منهما (أو كليهما) 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 مساحات الأسماء.

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

وهذا هو الأول من بلدي اثنين من الإجابة على هذا السؤال.

إذا كنت تريد دفع غرامة السيطرة على مساحات - على سبيل المثال إذا كنت ترغب في حذف بعض منها دون غيرها، أو إذا كنت تريد استبدال مساحة واحدة مع آخر، يمكنك القيام بذلك باستخدام <لأ href = "HTTP: / /msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributeoverrides.aspx "يختلط =" noreferrer "> 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>

ولقد جردت مساحات.

وهناك سؤال منطقي، <م> يمكنك تجريد جميع النطاقات من أنواع التعسفية أثناء التسلسل، دون المرور عبر تجاوزات واضحة؟ الجواب هو نعم، وكيف نفعل ذلك في ردي المقبلة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top