How to add xsi schemalocation to root c # object XmlSerializer

后端 未结 2 1284
情歌与酒
情歌与酒 2020-12-09 18:34

I am using XmlSerializer to create an object representing an XML file and now i want to add a schemalocation to the rootelement of my xml file. I can add namespaces like the

相关标签:
2条回答
  • 2020-12-09 18:55

    The XSD.exe generates partial classes, so you can add your own separate partial class to hold things like xsi:schemaLocation as fields or properties.

    So, adding to @Petru Gardea's sample elementB class, you only need to create another file in your project and add this partial class:

    public partial class elementB 
    {
        [XmlAttributeAttribute("schemaLocation", Namespace="http://www.w3.org/2001/XMLSchema-instance")]
        public string xsiSchemaLocation = "http://www.acme.com/xml/OrderXML-1-0.xsd";
    }
    

    There is one gotcha that I ran into doing this and that was by default xsd.exe does not add a namespace to the generated file(s). When you create this partial class of your own, it will most likely be in a namespace. Since <default namespace> and an explicitly defined namespace do not match, partial won't work. So, you need to use the namespace option on xsd.exe to actually get the generated classes into your namespace.

    0 讨论(0)
  • 2020-12-09 19:01

    Let's assume the following XSD:

    <?xml version="1.0" encoding="utf-8" ?>
    <!-- XML Schema generated by QTAssistant/XSD Module (http://www.paschidev.com) -->
    <xsd:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" xmlns="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:element name="elementB">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="FirstName" type="xsd:string"/>
                    <xsd:element name="LastName" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>  
    </xsd:schema>
    

    There are two ways at least to do it. The first one relies on inheritance and how you can play with the serializer annotations.

    xsd.exe generates this:

    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated by a tool.
    //     Runtime Version:4.0.30319.18034
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    using System.Xml.Serialization;
    
    // 
    // This source code was auto-generated by xsd, Version=4.0.30319.1.
    // 
    
    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/XMLSchema.xsd")]
    [System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/XMLSchema.xsd", IsNullable=false)]
    public partial class elementB {
    
        private string firstNameField;
    
        private string lastNameField;
    
        /// <remarks/>
        public string FirstName {
            get {
                return this.firstNameField;
            }
            set {
                this.firstNameField = value;
            }
        }
    
        /// <remarks/>
        public string LastName {
            get {
                return this.lastNameField;
            }
            set {
                this.lastNameField = value;
            }
        }
    }
    

    To "inject" the xsi:schemaLocation add a new class, elementA : elementB; notice:

    • System.Xml.Serialization.XmlRootAttribute setup
    • schemaLocation property setup.

    Test program:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml.Serialization;
    using System.IO;
    using System.Xml;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                elementB b = new elementB();
                b.FirstName = "P";
                b.LastName = "G";
    
                XmlSerializer ser = new XmlSerializer(typeof(elementB));
                StringBuilder sb = new StringBuilder();
                using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true })) 
                {
                    ser.Serialize(writer, b);
                }
                Console.WriteLine(sb.ToString());
    
                elementA a = new elementA();
                a.FirstName = "P";
                a.LastName = "G";
                a.schemaLocation = "http://tempuri.org/XMLSchema.xsd me";
                ser = new XmlSerializer(typeof(elementA));
                sb = new StringBuilder();
                using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
                {
                    ser.Serialize(writer, a);
                }
                Console.WriteLine(sb.ToString());
            }
        }
    }
    
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/XMLSchema.xsd")]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/XMLSchema.xsd", ElementName = "elementB", IsNullable = false)]
    public partial class elementA : elementB
    {
    
        private string torefField;
    
        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
        public string schemaLocation
        {
            get
            {
                return this.torefField;
            }
            set
            {
                this.torefField = value;
            }
        }
    }
    

    Generates the expected result:

    <?xml version="1.0" encoding="utf-16"?>
    <elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/XMLSchema.xsd">
      <FirstName>P</FirstName>
      <LastName>G</LastName>
    </elementB>
    <?xml version="1.0" encoding="utf-16"?>
    <elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://tempuri.org/XMLSchema.xsd me" xmlns="http://tempuri.org/XMLSchema.xsd">
      <FirstName>Petru</FirstName>
      <LastName>Gardea</LastName>
    </elementB>
    

    The second way relies on a custom writer that will inject what you want, wherever you want it (assuming the appropriate logic).

    You implement a custom XmlWriter:

    class MyXmlWriter : XmlWriter
    {
        XmlWriter _writer;
        bool _docElement = true;
    
        public string SchemaLocation { get; set; }
        public string NoNamespaceSchemaLocation { get; set; }
    
        public MyXmlWriter(XmlWriter writer)
        {
            _writer = writer;
        }
    
        (other methods omitted)
    
        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            _writer.WriteStartElement(prefix, localName, ns);
            if (_docElement)
            {
                if (!string.IsNullOrEmpty(SchemaLocation))
                {
                    _writer.WriteAttributeString("xsi", "schemaLocation", "http://www.w3.org/2001/XMLSchema-instance", SchemaLocation);
                }
                if (!string.IsNullOrEmpty(NoNamespaceSchemaLocation))
                {
                    _writer.WriteAttributeString("xsi", "noNamesapceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance", NoNamespaceSchemaLocation);
                }
                _docElement = false;
            }
        }
    
        (other methods omitted)
    
    }
    

    A modified test program:

    static void Main(string[] args)
    {
        elementB b = new elementB();
        b.FirstName = "P";
        b.LastName = "G";
    
        XmlSerializer ser = new XmlSerializer(typeof(elementB));
        StringBuilder sb = new StringBuilder();
        using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true })) 
        {
            ser.Serialize(writer, b);
        }
        Console.WriteLine(sb.ToString());
    
        sb = new StringBuilder();
    
        using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { Indent = true }))
        {
            MyXmlWriter newWriter = new MyXmlWriter(writer) { SchemaLocation = "http://tempuri.org/XMLSchema.xsd me" };
            ser.Serialize(newWriter, b);
        }
        Console.WriteLine(sb.ToString());
    }
    

    The result is the same...

    <?xml version="1.0" encoding="utf-16"?>
    <elementB xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/XMLSchema.xsd">
      <FirstName>P</FirstName>
      <LastName>G</LastName>
    </elementB>
    <?xml version="1.0" encoding="utf-16"?>
    <elementB xsi:schemaLocation="http://tempuri.org/XMLSchema.xsd me" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
      <FirstName>P</FirstName>
      <LastName>G</LastName>
    </elementB>
    
    0 讨论(0)
提交回复
热议问题