3

I am attempting to create a POST function that serialises C# class objects into XML.

The part I am having great difficulty with is adding namespace prefixes to the sub-root element's children, so in this instance, contact children only.

The only way I seem to be able to get the prefix onto the child elements of contactis to add them through SerializerNamespace class, however I can only get this to attach to the root element, CreateContact.

How can I achieve this?

XML Currently Produced:

<?xml version=\"1.0\"?>
<CreateContact xmlns:a="http://foo.co.uk/Contact" xmlns="http://foo.co.uk">
<a:contact>
<a:Email>stest@gmail.com</a:Email>
<a:FirstName>Simon</a:FirstName>
<a:LastName>Test</a:LastName>
<a:Phone>09088408501</a:Phone>
<a:Title>Mr</a:Title>
</a:contact>
</CreateContact>

Serialisation function:

public static void CreateContact(Contact contact)
{
    string tmp = url;
    string xml = "";
    string result = "";

    XmlDocument xd = new XmlDocument();

    var cc = new CreateContact();
    cc.contact = contact;
    var xs = new XmlSerializer(cc.GetType());

    XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
    xsn.Add("a", "http://foo.co.uk/Contact");

    using (MemoryStream ms = new MemoryStream())
    {
    xs.Serialize(ms, cc, xsn);
    ms.Position = 0;
    xd.Load(ms);
    xml = xd.InnerXml;
    }

    using (WebClient web = new WebClient())
    {
    web.Credentials = new NetworkCredential(username, password);
    web.Headers.Add("Content-Type", "application/xml");
    try
    {
        result = web.UploadString(tmp, "POST", xml);
    }
    catch (WebException ex)
    {
    }
    }
}

XML Class Constructs:

[Serializable()]
[XmlRoot(ElementName = "CreateContact", Namespace = "http://foo.co.uk")]
public class CreateContact
{
    [XmlElement(ElementName = "contact", Namespace = "http://foo.co.uk/Contact")]
    public Contact contact { get; set; }
}

[DataContract(Name = "Contact", Namespace = "http://foo.co.uk/Contact")]
[XmlType("a")]
public class Contact
{
    [XmlElement(ElementName = "Email", Namespace = "http://foo.co.uk/Contact")]
    [DataMember(Name = "Email")]
    public string Email { get; set; }
    [XmlElement(ElementName = "FirstName", Namespace = "http://foo.co.uk/Contact")]
    [DataMember(Name = "FirstName")]
    public string Firstname { get; set; }
    [XmlElement(ElementName = "LastName", Namespace = "http://foo.co.uk/Contact")]
    [DataMember(Name = "LastName")]
    public string Lastname { get; set; }
    [XmlElement(ElementName = "Phone", Namespace = "http://foo.co.uk/Contact")]
    [DataMember(Name = "Phone")]
    public string Phone { get; set; }
    [XmlElement(ElementName = "Title", Namespace = "http://foo.co.uk/Contact")]
    [DataMember(Name = "Title")]
    public string Title { get; set; }
}

XML Desired:

<?xml version=\"1.0\"?>
<CreateContact  xmlns="http://foo.co.uk">
<contact xmlns:a="http://foo.co.uk/Contact">
<a:Email>stest@gmail.com</a:Email>
<a:FirstName>Simon</a:FirstName>
<a:LastName>Test</a:LastName>
<a:Phone>09088408501</a:Phone>
<a:Title>Mr</a:Title>
</contact>
</CreateContact>
Andrea Antonangeli
  • 1,242
  • 1
  • 21
  • 32
PurpleSmurph
  • 2,055
  • 3
  • 32
  • 52
  • The two are semantically equivalent - is there any reason you actually need to move the namespace declaration? I doubt this is possible without some manual serialisation code or post-processing the XML after serialisation. – Charles Mager Dec 11 '15 at 15:49
  • 3
    In the "desired" XML, your `contact` element is actually in the `http://foo.co.uk` namespace - is that really what you want? – Jon Skeet Dec 11 '15 at 15:49
  • @CharlesMager: They're subtly different - the contact element is in a different namespace in the OP's desired XML, but I *suspect* that's a mistake... – Jon Skeet Dec 11 '15 at 15:50
  • @JonSkeet whoops, good spot. I'll admit I skimmed it a little! – Charles Mager Dec 11 '15 at 15:52
  • 1
    Charles & Jon - this is not a mistake, this is how the XML is requested in the API documentation I have been presented with. I was under the impression(sure I'm wrong) that the prefixes are not necessary and that the child elements will pick up the namespace of the parent unless told otherwise. – PurpleSmurph Dec 11 '15 at 15:53
  • In which case, Jon is correct - your 'desired' XML has `contact` in a different namespace to the one you've specified in the attributes on the class. Fix this and you might get what you want. – Charles Mager Dec 11 '15 at 15:54
  • Charles & Jon yes, stupid mistake, thought that was correct syntax, clearly not, thank you both! – PurpleSmurph Dec 11 '15 at 16:02
  • We don't put solutions in the question. You should know that by now. – LarsTech Dec 11 '15 at 16:22

1 Answers1

4

As alluded in the comments, the reason for the difference is that contact should be in the namespace http://foo.co.uk, not http://foo.co.uk/Contact.

As an aside, a couple of further comments:

  • You probably don't need the DataMember attributes, unless you're using DataContractSerializer somewhere else.
  • Most of the Xml* attributes are superfluous here, and could be removed or consolidated by inheriting from XmlRoot.
  • If all you need is the XML string, you'd be better off serialising to something like a StringWriter rather than to a stream and then loading into the DOM just to get the text (see this question if you need the XML declaration to specify utf-8)

So, you'd get the XML as below:

var xsn = new XmlSerializerNamespaces();
xsn.Add("a", "http://foo.co.uk/Contact");

var xs = new XmlSerializer(typeof(CreateContact));

using (var stringWriter = new StringWriter())
{
    xs.Serialize(stringWriter, cc, xsn);
    xml = stringWriter.ToString();
}

With your classes defined as:

[XmlRoot(ElementName = "CreateContact", Namespace = "http://foo.co.uk")]
public class CreateContact
{
    [XmlElement(ElementName = "contact")]
    public Contact Contact { get; set; }
}

[XmlRoot("contact", Namespace = "http://foo.co.uk/Contact")]
public class Contact
{
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
    public string Title { get; set; }
}

See this fiddle for the complete example.

Community
  • 1
  • 1
Charles Mager
  • 25,735
  • 2
  • 35
  • 45
  • Much better explanation, thanks. The data members in my example are legacy and have to be there, but I could not get them to work in POST situations – PurpleSmurph Dec 11 '15 at 16:19