jvance.com

.net, c#, asp.net, linq, htpc, woodworking

Jarrett's Tech Blog - Browsing WCF

  1. WCF Adds Root Node on IXmlSerializable Object

    Has anybody else had trouble with WCF adding a root node on their objects that implement IXmlSerializable?

    Here is the setup:

    • Create a class that implements IXmlSerializable
    public class Entry : IXmlSerializable
    {
      public XElement Xml { get; set; }
      public void ReadXml(XmlReader reader)
      {
        Xml = XElement.Load(reader, LoadOptions.SetBaseUri);
      }
      public void WriteXml(XmlWriter writer)
      {
        Xml.WriteTo(writer);
      }
    }
    • Create a WCF service that returns the object.
    [ServiceContract]
    [XmlSerializerFormat]
    public interface IService
    {
      [OperationContract]
      [WebGet(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{a}/{b}/{c}")]
      Entry RetrieveEntry(string a, string b, string c);
    }
    
    public class Service: IService
    {
      public Entry RetrieveEntry(string a, string b, string c)
      {
        return new Entry
        {
          Xml = new XElement("test", a + "/" + b + "/" + c);
        }
      }
    }
    When hitting the service located at http://localhost/EntryService.svc/one/two/three, I expect to get the following xml:
    <test>one/two/three</test>
    Instead, I always get the above xml wrapped in a root node as shown below:
    <Entry xmlns="http://schemas.datacontract.org/2004/07/EntryTest">
      <test>one/two/three</test>
    </Entry>
    1. I've set the BodyStyle to Bare
    2. I've set the [XmlSerializerFormat] attribute
    3. I've tried putting [XmlRoot(null)] on the Entry class
    The only thing that seems to work is changing the return type to XElement. This is really odd behavior of WCF. I can't find anything in the documentation as to why it is doing this.

    Update (7/27): It appears you must use the XmlRootAttribute to accomplish this. Your WriteXml(XmlWriter writer) method can check to see if the root node was already added by the serializer, and if not add it:

    public void WriteXml(XmlWriter writer)
    {
    
        //only start document if needed
        bool start = false;
        if (writer.WriteState == WriteState.Start)
        {
            start = true;
            writer.WriteStartDocument();
            writer.WriteStartElement("root", "http://example.com");
        }
        //TODO: custom serialization here
    
        if (start)
        {
            writer.WriteEndElement();
            writer.WriteEndDocument(); 
        }
    }
    This allows the WriteXml to be usable even when the object isn't being serialized by the WCF serializer.
    Posted by Jarrett on July 26 at 10:27 PM

© Copyright 2024