NealWalters NealWalters - 3 months ago 39
JSON Question

How to create JSON to match/serialize to DataContract in WCF Rest Service

Interface:

namespace SQRT_WCF
{
[DataContract]
public class PlaceOrder
{
[DataMember]
public string claimID { get; set; }
[DataMember]
public string rederenceDate { get; set; }
}
}


C# Method invoked:

public SQ_jsonModel.Response placeOrder(PlaceOrder argPlaceOrderJSON)
{
...
}


I've attached my webservice to the worker processes, and tracing through the code. So far, everything I've tried, the variable argPlaceOrderJSON is null.

I've tried at least three variations:

Variation #1

{
"claimID" : "value",
"rederenceDate" : "value"
}


Variation #2:

{
"PlaceOrder" : {
"claimID" : "value",
"rederenceDate" : "value"
}


Variation #3:

{
"ns0:PlaceOrder" : {
"@xmlns:ns0" : "SQRT_WCF",
"claimID" : "value",
"rederenceDate" : "value"
}


I wrote a quick C# console program to build and serialize an object and it matches Variation 1 above. So now I'm thinking the problem is not the JSON, but something with the handshake between SOAP-UI and the web service. I'm going to write a C# test client next to see what happens there.

SQRT_WCF.PlaceOrder order = new SQRT_WCF.PlaceOrder();
order.ClaimID = "claim1";
string strJson = new JavaScriptSerializer().Serialize(order);


Someone asked for attributes in webservice, assuming this is what he meant:

public interface ISQRTREST
{
[OperationContract]
void DoWork();

[OperationContract]
[WebInvoke(Method="GET",
ResponseFormat = WebMessageFormat.Json ,
BodyStyle = WebMessageBodyStyle.Wrapped ,
UriTemplate ="json/{id}"
)]
string JSONData(String id);


[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "placeOrder/"
)]
SQ_jsonModel.Response placeOrder(PlaceOrder order);


}

Web.Config

<?xml version="1.0"?>
<configuration>

<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="SQRT_WCF.SQRTREST" behaviorConfiguration="ServiceBehaviour">
<endpoint address="" binding="webHttpBinding" bindingConfiguration="longTimeoutBinding" contract="SQRT_WCF.ISQRTREST" behaviorConfiguration="web">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="longTimeoutBinding"
receiveTimeout="00:10:00" sendTimeout="00:10:00">
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

</configuration>


UPDATE:
Seems like changing BodyStyle = WebMessageBodyStyle.Bare got it working with variation 1.

But now, the problem is I need to be able to get it working with WebMessageBodyStyle.Wrapped.

This is an existing web service that it in production. The person who wrote the program is long gone, and now the B2B/BizTalk team is taking over the project. We are using BT2010 which doesn't have REST yet, and we are trying to provide the same interface to our customer, so they don't have to change anything other than the URL.

RESTful web service body format
seems to explain fairly well, I'm about to try Variation 2 with the .wrapped option in C#.

Update 2:
Trying Variation 2 with BodyWrapped is still getting a null object:

This is how I wrapped it. Took what worked in Update1

string strPostBodyWrapped = "{\"PlaceOrder\" : " + strPostBody + "}";


Will do more debugging after lunch.

Update 3:
Still no luck. Based on above StackOverflow ref, it looks the wrapper should be "entity", which I tried several ways:

From SOAP-UI:

{
"entity" : {
"claimID" : "value",
}


I also tried "Entity" instead of "entity", just be sure.

All the fields in my DataContract are string, so there should be no issue serializes dates or numbers.

From C# Client tester program:

strPostBody = replaceSingleWithDoubleQuotes("{'ClaimID':'testvalue'}");

string strPostBodyWrapped = replaceSingleWithDoubleQuotes("{'entity': ") + strPostBody + "}";

public static string replaceSingleWithDoubleQuotes(string argText)
{
return argText.Replace("'", "\"");
}


If I try something bad in SOAP_UI, like this:

{
"entity" : { xxx
"claimID" : "value",
}


Then I realized entity was not a keyword but the name of his class... oops.
So I have tried both "Placeorder" and "placeorder" as my wrapper for my case.

I get a nice error like this:

The server encountered an error processing the request. The exception message is 'The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation 'placeOrderSQRT'. Encountered unexpected character 'x'.'...


So I don't understand why in this case the parser fails, but in other cases it doesn't fail, but it doesn't set any field values either.

If I try invalid field names, but correct syntax, the parser seems to not give any error, e.g. "claimIDxxxxxxxx" : "value"

Answer

Answer was buried here:

WCF BodyStyle WrappedRequest doesn't work for incoming JSON param?

The name of the wrapper is not the parameter type, but the parameter name.

So in my case, the wrapper name was "order", because my parm name is "order":

SQ_jsonModel.Response placeOrderSQRT(PlaceOrder order);

The following works from SOAP-UI now:

{
    "order" : {
         "claimID" : "value",
         "rederenceDate" : "value"
}

To me it's very shocking; normally we can change variable names and parms names (just not Class Names) with absolute no impact on the user interface. The reference post shows how you can use the attribute "MessageParameter" to set the external name, and still allow one to change the internal name if needed.

Comments