4

How can I generate, for example, this XML in C#

<?xml version='1.0'?>
<oneshot xmlns='http://www.w3.org/2002/xforms' xmlns:dm='http://mobileforms.foo.com/xforms' xmlns:h='http://www.w3.org/1999/xhtml' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
  <dm:form_namespace>Foo</dm:form_namespace>
  <Days>6</Days>
  <Leave_Type>Option 3</Leave_Type>
</oneshot>

I'm specifically struggling with the xmlns:dm declaration. Any ideas?

3
  • "Serialize" as "need to create class that will produce given XML when XML serialization is used" or simply "how to create XML with nodes in different namespaces"? Commented Feb 21, 2013 at 0:11
  • Can you post the oneshot object? Commented Feb 21, 2013 at 0:28
  • @AlexeiLevenkov Fair call. I've updated the title as this isn't technically serialization. Commented Feb 21, 2013 at 13:55

5 Answers 5

3

Your best bet (read: minimal amount of hacks) is probably going to be a custom IXmlSerializable implementation; you can get part-way to what you want via combinations of XmlRootAttribute, XmlElementAttribute, etc, like so:

[Serializable]
[XmlRoot("oneshot")]
public class OneShot 
{
    [XmlElement("form_namespace", Namespace="http://mobileforms.foo.com/xforms")]
    public string FormNamespace {get; set;}
    [XmlElement("Days")]
    public int Days {get; set;}
    [XmlElement("Leave_Type")]
    public string LeaveType {get; set;}

Which will generate something like:

<?xml version="1.0" encoding="utf-16"?>
<oneshot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <form_namespace xmlns="http://mobileforms.foo.com/xforms">Foo</form_namespace>
  <Days>6</Days>
  <Leave_Type>Option 3</Leave_Type>
</oneshot>

But if you implement IXmlSerializable, you have full control:

public class OneShot : IXmlSerializable
{
    public string FormNamespace {get; set;}
    public int Days {get; set;}
    public string LeaveType {get; set;}

    #region IXmlSerializable
    public void WriteXml (XmlWriter writer)
    {
        writer.WriteStartElement("oneshot");
        writer.WriteAttributeString("xmlns", null, "http://www.w3.org/2002/xforms");
        writer.WriteAttributeString("xmlns:dm", null, "http://mobileforms.foo.com/xforms");
        writer.WriteAttributeString("xmlns:h", null, "http://www.w3.org/1999/xhtml");
        writer.WriteAttributeString("xmlns:xsd", null, "http://www.w3.org/2001/XMLSchema");
        writer.WriteElementString("dm:form_namespace", null, FormNamespace);
        writer.WriteElementString("Days", Days.ToString());
        writer.WriteElementString("Leave_Type", LeaveType);
        writer.WriteEndElement();
    }

    public void ReadXml (XmlReader reader)
    {
        // populate from xml blob
    }

    public XmlSchema GetSchema()
    {
        return(null);
    }
    #endregion
}

Which gives you:

<?xml version="1.0" encoding="utf-16"?>
<OneShot>
  <oneshot xmlns="http://www.w3.org/2002/xforms" xmlns:dm="http://mobileforms.foo.com/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <dm:form_namespace>Foo</dm:form_namespace>
    <Days>6</Days>
    <Leave_Type>Option 3</Leave_Type>
  </oneshot>
</OneShot>
Sign up to request clarification or add additional context in comments.

6 Comments

All looks very promising. However, in practice I get an exception on writing the first attribute string "The prefix '' cannot be redefined from '' to 'w3.org/2002/xforms' within the same start element tag." I then get one on the second attribute string "Invalid name character in 'xmlns:dm'. The ':' character, hexadecimal value 0x3A, cannot be included in a name."
@MattDell Add what you're trying to your question? It sounds like it might be a usage issue...
@MattDell, consider using 4 argument version of WriteElementString which takes namespace prefix (I'll post sample as separate answer, but if you like it credit should go to this one...)
@JerKimball I'm trying to use this API: docs.devicemagic.com/push-api I can't seem to generate a valid XML document to post to it.
@MattDell I mean, add your version of WriteXml - more likely than not, there's a better/different override to use (as Alexi mentioned)
|
1

One way to write XML with nodes in different namespaces is to use 4-argument version of XmlWriter.WriteElementString to explicitly specify namespace and prefixes the way you want:

var s = new StringWriter();
using (var writer = XmlWriter.Create(s))
{
    writer.WriteStartElement("oneshot", "http://www.w3.org/2002/xforms");
    writer.WriteElementString("dm", "form_namespace", 
         "http://mobileforms.foo.com/xforms","Foo");
    // pick "http://www.w3.org/2002/xforms" by default for Days node
    writer.WriteElementString("Days", "6");
    // or you can explicitly specify "http://www.w3.org/2002/xforms"
    writer.WriteElementString("Leave_Type", 
         "http://www.w3.org/2002/xforms", "Option 3");
    writer.WriteEndElement();
}

Console.Write(s.ToString());

Note that your sample XML defines more prefixes than are used in the XML. If your requirement is to produce "text identical XML" (vs. identical from XML point of view, but not necessary represented with identical text) you may need to put more effort in adding namespace prefixes and xmlns attributes in places you need.

Note 2: creating XML object first (XDocument for modern/LINQ way, or XmlDocument if you like DOM more) may be easier approach.

Comments

0

Try using the XAML serializer from .NET 4.0+'s assembly System.Xaml.

You may need to add attributes to mark properties as content instead of XML attributes.

Comments

0

Thanks, all, for the help! It was a combination of two of the above answers that did it for me. I'm going to set the one that suggested the IXmlSerializable approach as that was the majority of the solution.

I had to declare the XMLRoot tag on the class name, remove WriteStartElement and WriteEndElement, and then use the four parameter declaration.

This is the class that worked in the end:

[Serializable]
[XmlRoot("oneshot")]
public class LeaveRequestPush : IXmlSerializable
{
    public string FormNamespace { get; set; }
    public int Days { get; set; }
    public string LeaveType { get; set; }

    #region IXmlSerializable

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteElementString("dm", "form_namespace", "http://mobileforms.devicemagic.com/xforms", FormNamespace);
        writer.WriteElementString("Days", Days.ToString());
        writer.WriteElementString("Leave_Type", LeaveType);
    }
}

public void ReadXml (XmlReader reader)
{
    // populate from xml blob
}

public XmlSchema GetSchema()
{
    return(null);
}

Again, thanks all for the combined efforts. I would not have got this one by myself!

1 Comment

What about multiple namespaces? Wasn't that your question about? How did you solve it?
0

This code does the trick!

public void WriteXml(XmlWriter writer)
        {
            const string ns1 = "http://firstline.com/";
            const string xsi = "http://www.w3.org/2001/XMLSchema-instance";
            writer.WriteStartElement("myRoot", ns1);
            writer.WriteAttributeString("SchemaVersion", "1.0");
            writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);
            writer.WriteAttributeString("xsi", "schemaLocation", xsi, ns1 + " schema1.xs");

        writer.WriteStartElement("element1", ns1);
        writer.WriteElementString("test1", ns1, "test value");
        writer.WriteElementString("test2", ns1, "value 2");
        writer.WriteEndElement();
        writer.WriteEndElement();//to close classname that has root xml
}

Xml with multiple awesome namespaces!

<?xml version="1.0" encoding="utf-16"?>
<myClassNameWhereIXmlSerializableIsImplemented>
  <myRoot SchemaVersion="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://firstline.com/ schema1.xs" xmlns="http://firstline.com/">
    <element1>
      <test1>test value</test1>
      <test2>value 2</test2>
    </element1>
  </myRoot>
</myClassNameWhereIXmlSerializableIsImplemented>

There is something confusing about this line

writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);

if you give a random url instead of "http://www.w3.org/2000/xmlns/" , it will fail. The url that appear in xml is actually from "xsi" variable. One more example to prove the point

writer.WriteAttributeString("xml", "base", "http://www.w3.org/XML/1998/namespace", base1);

where base1 = "<custom url>"

and if you want to output with a prefix like this <d:test1>somevalue<\d:test1> then writer.WriteElementString("d","test1", ns1, "somevalue"); if ns1 is not defined above then it will be added in xml output.

but for <d:test1> <blah>... <\d:test1> StartElement is needed writer.WriteStartElement("test1", ns1); to be closed when needed with writer.WriteEndElement();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.