简体   繁体   中英

Serialization to XML using REST and WCF

I am having problem with using REST and returning response as an XML. I've created basic service from the template and everything looks nice but when I want to serialize my class and return it as a responce the service returns something else.

Take a look:

[WebHelp(Comment = "Sample description for DoWork")]
[WebInvoke(UriTemplate = "DoWork")]
[OperationContract]
public SampleResponseBody DoWork(SampleRequestBody request)
{
    //TODO: Change the sample implementation here
    return new SampleResponseBody()
    {
        Value = String.Format("Sample DoWork response: '{0}'", request.Data)

    };
}

[WebHelp(Comment = "Returns order state based on client and order number")]
[WebInvoke(UriTemplate = "OrderStatus")]
[OperationContract]
public order_status OrderStatus(q_order_status request)
{   
    return new order_status() 
    {
        error_id = 0,
        client_acr = "client", 
        order_acr = "order"
    };
}

The first method is from the template, the second is mine. Returning structures look like this:

public class SampleResponseBody
{
    [DataMember]
    public string Value { get; set; }
}

public class q_order_status
{
    public string client_acr;
    public string order_acr;
}

[DataContract]
[XmlSerializerFormat]
public class order_status
{
    [XmlAttribute]
    public int error_id;
    [XmlElement]
    public string error_desc;
    [XmlElement]
    public string order_acr;
    [XmlElement]
    public string client_acr;
}

Edited:

When I am on the help page of the REST kit, I am getting this as a sample response for both methods which is wrong (I should not get this for the second method):

<SampleResponseBody>
<Value>String content</Value>
</SampleResponseBody>

When I call first method like this:

User-Agent: Fiddler
Host: ipv4.fiddler:4617
Content-Type: text/xml
Content-Length: 63

<SampleRequestBody>
<Data>bla bla</Data>
</SampleRequestBody>

I receive:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 30 Sep 2009 09:41:20 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 141
Connection: Close

<SampleResponseBody xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Value>Sample DoWork response: 'bla bla'</Value></SampleResponseBody>

Whis is ok.

WHen I call second method like this:

User-Agent: Fiddler
Host: ipv4.fiddler:4617
Content-Type: text/xml
Content-Length: 115

<q_order_status>
<client_acr>String content</client_acr>
<order_acr>String content</order_acr>
</q_order_status>

I am getting this:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 30 Sep 2009 09:44:18 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Length: 67
Connection: Close

<order_status xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>

And it should return a serialized to XML instance of class order_status What is wrong?

Thanks in advance.

After edit: ok, the problem was that for [OperationContract] XmlSerializer wasn't triggered. [XmlSerializerFormat] must be inserted right after the [OperationContract] to override default DataContractSerializer .

With the WCF REST Starter Kit, you should be able to create a method that returns an XElement as its return value:

[WebHelp(Comment = "Returns order state based on client and order number")]
[WebInvoke(UriTemplate = "OrderStatus")]
[OperationContract]
public XElement OrderStatus(q_order_status request)
{   
  .....
}

In that case, your method implementation could look something like this:

public XElement OrderStatus(q_order_status request)
{   
    return new XElement("q_order_status",
                 new XAttribute("error_id", 0),
                 new XElement("client_acr", "client acr value"),
                 new XElement("order_acr", "order acr value")
           );
}

This would return an XML fragment like this:

<q_order_status error_id="0">
  <client_acr>client acr value</client_acr>
  <order_acr>order acr value</order_acr>
</q_order_status>

This way, anything really is possible - it's totally up to you how and what to create in terms of XML structure.

Marc

I would say that q_order_status should be decorated with [DataContract] attribute, and all of his members (along with those of order_status) should be decorated with [DataMember] attribute. Or did you omit these on purpose?

Can you try with this order_status code:

[DataContract]
public class order_status
{
    [DataMember]
    public int error_id;
    [DataMember]
    public string error_desc;
    [DataMember]
    public string order_acr;
    [DataMember]
    public string client_acr;
}

Side note: I would also suggest that you follow .NET naming conventions for classes and members, PascalCase and no underscore. If you are restricted to a given xml name, you can use the Name member of the DataContract/DataMember attribute to define the xml name. (eg: [DataContract(Name="order_status")] public class OrderStatus). ;)

This question (and the last edit) helped us to solve a similar problem. Instead of decorating thousands of data elements with DataContract/DataMember attributes and using the (default) DataContractSerializer, we found that if our WCF service used the XmlSerializerFormat instead, we could easily deserialize our objects.

For those who need an example:

[System.ServiceModel.ServiceContract]
public interface IRestService
{
    [System.ServiceModel.OperationContract]
    // Added this attribute to use XmlSerializer instead of DataContractSerializer
    [System.ServiceModel.XmlSerializerFormat(
        Style=System.ServiceModel.OperationFormatStyle.Document)]
    [System.ServiceModel.Web.WebGet(
        ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml,
        UriTemplate = "xml/objects/{myObjectIdentifier}")]
    MyObject GetMyObject(int myObjectIdentifier);
}

This is how we're deserializing the objects:

public static T DeserializeTypedObjectFromXmlString<T>(string input)
{
    T result;

    try
    {
        System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
        using (System.IO.TextReader textReader = new System.IO.StringReader(input))
        {
            result = (T)xs.Deserialize(textReader);
        }
    }
    catch
    {
        throw;
    }

    return result;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM