Populating ANY elements on a web service in C#

后端 未结 3 1437
误落风尘
误落风尘 2020-12-21 04:42

Can someone point me to a C# example where they populate a request and receive the response for a web service where the schema is mostly implemented as ANY elements?

相关标签:
3条回答
  • 2020-12-21 05:28

    XmlNode is a type in the old XML Document Object Model that dates from c# 1.1. It has been superseded by the much easier to use LINQ to XML object model. What is not well known is that [XmlAnyElementAttribute] actually supports this API. Thus in your GenericRequestData_Type type you could manually change the Any property to be of type System.Xml.Linq.XElement[]:

    public class GenericRequestData_Type
    {
        private System.Xml.Linq.XElement[] anyField;
    
        [System.Xml.Serialization.XmlAnyElementAttribute()]
        public System.Xml.Linq.XElement[] Any
        {
            get
            {
                return this.anyField;
            }
            set
            {
                this.anyField = value;
            }
        }
    }
    

    Then you can initialize the array easily as follows, following the instructions in Creating XML Trees in C# (LINQ to XML):

    // Taken from 
    // https://msdn.microsoft.com/en-us/library/mt693096.aspx
    var contacts =
        new XElement("Contacts",
            new XElement("Contact",
                new XElement("Name", "Patrick Hines"),
                new XElement("Phone", "206-555-0144"),
                new XElement("Address",
                    new XElement("Street1", "123 Main St"),
                    new XElement("City", "Mercer Island"),
                    new XElement("State", "WA"),
                    new XElement("Postal", "68042")
                )
            )
        );
    
    var genericRequest = new GenericRequestData_Type();
    genericRequest.Any = new[] { contacts };
    

    Sample fiddle.

    0 讨论(0)
  • 2020-12-21 05:30

    XmlNode is an abstract base type. You can create concrete XmlElement objects and add then to your Any array following the instructions in Create New Nodes in the DOM:

    • First, create an XmlDocument. While the document does not appear explicitly in the XmlElement [] Any array, it is needed to subsequently create elements.
    • Create elements using XmlDocument.CreateElement()
    • Add the created elements to their parents as appropriate using XmlNode.AppendChild
    • Set text values by using XmlElement.InnerText.

    Thus the following will work:

    var dom = new XmlDocument();
    var contacts = dom.CreateElement("Contacts");
    dom.AppendChild(contacts);
    var contact = dom.CreateElement("Contact");
    contacts.AppendChild(contact);
    var name = dom.CreateElement("Name");
    contact.AppendChild(name);
    name.InnerText = "Patrick Hines";
    var phone = dom.CreateElement("Phone");
    contact.AppendChild(phone);
    phone.InnerText = "206-555-0144";
    var address = dom.CreateElement("Address");
    contact.AppendChild(address);
    var street1 = dom.CreateElement("Street1");
    address.AppendChild(street1);
    street1.InnerText = "123 Main St";
    var city = dom.CreateElement("City");
    address.AppendChild(city);
    city.InnerText = "Mercer Island";
    var state = dom.CreateElement("State");
    address.AppendChild(state);
    state.InnerText = "WA";
    var postal = dom.CreateElement("Postal");
    address.AppendChild(postal);
    postal.InnerText = "68042";
    
    var genericRequest = new GenericRequestData_Type();
    genericRequest.Any = new[] { dom.DocumentElement };
    

    Sample fiddle.

    0 讨论(0)
  • 2020-12-21 05:36

    Your question is fairly general so I'm going to narrow it down a bit to the following situation: you have a type with an [XmlAnyElement] member like so:

    public class GenericRequestData_Type
    {
        private System.Xml.XmlElement[] anyField;
    
        [System.Xml.Serialization.XmlAnyElementAttribute()]
        public System.Xml.XmlElement[] Any
        {
            get
            {
                return this.anyField;
            }
            set
            {
                this.anyField = value;
            }
        }
    }
    

    And you would like to initialize the Any field to the following XML:

    <Contacts>
      <Contact>
        <Name>Patrick Hines</Name>
        <Phone>206-555-0144</Phone>
        <Address>
          <Street1>123 Main St</Street1>
          <City>Mercer Island</City>
          <State>WA</State>
          <Postal>68042</Postal>
        </Address>
      </Contact>
    </Contacts>
    

    How can this be done?

    There are several options, so I'll break it down into a few different answers.

    Firstly, you could design c# classes corresponding to your desired XML using a code-generation tool such as http://xmltocsharp.azurewebsites.net/ or Visual Studio's Paste XML as Classes. Then you can serialize those classes directly to an XmlNode hierarchy using XmlSerializer combined with XmlDocument.CreateNavigator().AppendChild().

    First, introduce the following extension methods:

    public static class XmlNodeExtensions
    {
        public static XmlDocument AsXmlDocument<T>(this T o, XmlSerializerNamespaces ns = null, XmlSerializer serializer = null)
        {
            XmlDocument doc = new XmlDocument();
            using (XmlWriter writer = doc.CreateNavigator().AppendChild())
                new XmlSerializer(o.GetType()).Serialize(writer, o, ns ?? NoStandardXmlNamespaces());
            return doc;
        }
    
        public static XmlElement AsXmlElement<T>(this T o, XmlSerializerNamespaces ns = null, XmlSerializer serializer = null)
        {
            return o.AsXmlDocument(ns, serializer).DocumentElement;
        }
    
        public static T Deserialize<T>(this XmlElement element, XmlSerializer serializer = null)
        {
            using (var reader = new ProperXmlNodeReader(element))
                return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
        }
    
        /// <summary>
        /// Return an XmlSerializerNamespaces that disables the default xmlns:xsi and xmlns:xsd lines.
        /// </summary>
        /// <returns></returns>
        public static XmlSerializerNamespaces NoStandardXmlNamespaces()
        {
            var ns = new XmlSerializerNamespaces();
            ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
            return ns;
        }
    }
    
    public class ProperXmlNodeReader : XmlNodeReader
    {
        // Bug fix from this answer https://stackoverflow.com/a/30115691/3744182
        // To http://stackoverflow.com/questions/30102275/deserialize-object-property-with-stringreader-vs-xmlnodereader
        // By https://stackoverflow.com/users/8799/nathan-baulch
        public ProperXmlNodeReader(XmlNode node) : base(node) { }
    
        public override string LookupNamespace(string prefix)
        {
            return NameTable.Add(base.LookupNamespace(prefix));
        }
    }
    

    Next, define types corresponding to your <Contacts> hierarchy:

    [XmlRoot(ElementName = "Address")]
    public class Address
    {
        [XmlElement(ElementName = "Street1")]
        public string Street1 { get; set; }
        [XmlElement(ElementName = "City")]
        public string City { get; set; }
        [XmlElement(ElementName = "State")]
        public string State { get; set; }
        [XmlElement(ElementName = "Postal")]
        public string Postal { get; set; }
    }
    
    [XmlRoot(ElementName = "Contact")]
    public class Contact
    {
        [XmlElement(ElementName = "Name")]
        public string Name { get; set; }
        [XmlElement(ElementName = "Phone")]
        public string Phone { get; set; }
        [XmlElement(ElementName = "Address")]
        public Address Address { get; set; }
    }
    
    [XmlRoot(ElementName = "Contacts")]
    public class Contacts
    {
        [XmlElement(ElementName = "Contact")]
        public Contact Contact { get; set; }
    }
    
    [XmlRoot("GenericRequestData_Type")]
    public class Person
    {
        public string Name { get; set; }
        public DateTime BirthDate { get; set; }
        [XmlArray("Emails")]
        [XmlArrayItem("Email")]
        public List<string> Emails { get; set; }
        [XmlArray("Issues")]
        [XmlArrayItem("Id")]
        public List<long> IssueIds { get; set; }
    }
    

    Finally, initialize your GenericRequestData_Type as follows:

    var genericRequest = new GenericRequestData_Type();
    
    var contacts = new Contacts
    {
        Contact = new Contact
        {
            Name = "Patrick Hines",
            Phone = "206-555-0144",
            Address = new Address
            {
                Street1 = "123 Main St",
                City = "Mercer Island",
                State = "WA",
                Postal = "68042",
            },
        }
    };
    genericRequest.Any = new[] { contacts.AsXmlElement() };
    

    Sample fiddle.

    0 讨论(0)
提交回复
热议问题