user1886415
user1886415

Reputation: 485

WCF Restful Webservice serializing Datatable as XML

I'm using a WCF restful webservice with framework 4.0. I want to serialize a datatable as XML and return the resulting XML. I have this working, but I can't help but feel there is a better way.

I initially started out doing the following:

[WebGet(UriTemplate = "")]
public DataTable helloWorld()
{
    using (DataTable dt = new DataTable("Test"))
    {
        dt.Columns.Add("Message");
        dt.Rows.Add(dt.NewRow());
        dt.Rows[0]["Message"] = "Hello World";

        return dt;
    }
}

Which gave me the following undesired results:

<DataTable xmlns="http://schemas.datacontract.org/2004/07/System.Data">
  <xs:schema id="NewDataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns=""     xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Test" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Test">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Message" type="xs:string" minOccurs="0"/>
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <DocumentElement xmlns="">
      <Test diffgr:id="Test1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
        <Message>Hello World</Message>
      </Test>
    </DocumentElement>
  </diffgr:diffgram>
</DataTable>

After a bit of tinkering I came up with the following code, which is a bit clunky. Is there a better way? Why can't I dispose the memory stream? Do I have a memory leak?

[WebGet(UriTemplate = "")]
public Stream helloWorld()
{
    WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";

    using (DataTable dt = new DataTable("Test"))
    {
        dt.Columns.Add("Message");
        dt.Rows.Add(dt.NewRow());
        dt.Rows[0]["Message"] = "Hello World";

        using (StringWriter sw = new StringWriter())
        {
            dt.WriteXml(sw, System.Data.XmlWriteMode.IgnoreSchema, false);

            MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sw.ToString()));
            return ms;

            //this fails for some reason
            //using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sw.ToString())))
            //    return ms;
        }
    }
}

This code gives me the desired result (I'm not picky about the root tag):

<DocumentElement>
  <Test>
    <Message>Hello World</Message>
  </Test>
</DocumentElement>

Upvotes: 2

Views: 3368

Answers (2)

Marcel N.
Marcel N.

Reputation: 13976

I recommend using WCF Data Services to do this. They implement the OData protocol, meaning they are built to expose data in a consistent and portable way. Serialization can be done to either JSON or XML.

Samples:

  1. Beginner level.
  2. A more comprehensive explanation and sample.

Upvotes: 1

RAN
RAN

Reputation: 598

I would suggest using a DataContract for the serialization:

This is a good tutorial on how to do this: http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide

You create a contract interface where you define all the necessary methods.

In your case something like this:

namespace MyProject.HelloWorld {
    [ServiceContract]
    public interface IHelloWorld {
        [OperationContract]
        [WebGet(UriTemplate="", ResponeFormat=WebMessageFormat.Xml)]
        HelloWorldResult helloWorld();
    }

    [DataContract()]
    public class HelloWorldResult {
        [DataMember]
        public String Message {get; set;}
    }
}

Just implement the Interface IHelloWorld in your Service.svc and return a new Instance of a HelloWorldResult class:

public HelloWorldResult helloWorld() {
   return new HelloWorldResult() {
       Message = "Hello World!"
   };
}

But you must tell the endpoint to use the IHelloWorld Service Contract as the contract (see tutorial: http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide --> Step 6)

Hope this helps

cheers

Upvotes: 0

Related Questions