I'm trying to post to my WCF Service from an iPhone app. I have the app posting to the service and trying to send data in the form of a service data contract in XML like below:
NSData *myPostData = [[NSString stringWithFormat:#"<AddMediaItem xmlns='http://www.example.com'><Item xmlns:a='http://www.example.com/MediaItem'><a:MediaType>iPhone</a:MediaType><a:Description>Description</a:Description><a:Name>Test</a:Name><a:ImageType>JPEG</a:ImageType></Item></AddMediaItem>"] dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *myMutablePostData = [NSMutableData dataWithData:myPostData];
[request setPostBody:myMutablePostData];
[request setRequestMethod:#"POST"];
[request addRequestHeader:#"Content-Type" value:#"application/xml"];
[request setDidFinishSelector:#selector(uploadFinished:)];
[request setDidFailSelector:#selector(uploadFailed:)];
[request setDelegate:self];
[request startAsynchronous];
For testing purposes, I've setup my service to return the data contract that I send to the service. However, when it returns, some of the values seem to be NULLs although from the code above, I am giving them values.
What also seems strange is that the Name data member always has a value, but the other 3 I'm sending return NULL or with the correct values based on the order they appear in the XML I post to the service.
Below is the code for my service contract:
[ServiceContract(Namespace = "http://www.example.com")]
public interface IImageDiaryService
{
[OperationContract]
[WebInvoke(UriTemplate = "AddMediaItem", Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
MediaItem AddMediaItem(MediaItem Item);
}
Below is my code for my MediaItem data contract:
[DataContract(Namespace = "http://www.example.com/MediaItem")]
public class MediaItem
{
[DataMember]
public Int32 Id { get; set; }
[DataMember]
public String Name { get; set; }
[DataMember]
public String Description { get; set; }
[DataMember]
public String ImageData { get; set; }
[DataMember]
public String ImageType { get; set; }
[DataMember]
public String MediaType { get; set; }
}
Do let me know if you need any further code.
Any help is much appreciated.
The order is important in data contracts - by default the data members are ordered alphabetically, but you can override it by using the Order property of [DataMember]. Try reordering the fields and you'll get the values populated correctly
<AddMediaItem xmlns='http://www.example.com'>
<Item xmlns:a='http://www.example.com/MediaItem'>
<a:Description>Description</a:Description>
<a:ImageType>JPEG</a:ImageType>
<a:MediaType>iPhone</a:MediaType>
<a:Name>Test</a:Name>
</Item>
</AddMediaItem>
The DataContractSerializer only deserializes in the known order. As carlosfigueira says, default is alphabetical but you can specify an order. Yours aren't alphabetical and so I would expect that you are having server-side serialization issues.
I'd suggest you log what's happening server side. Is the request deserializing correctly inside your service? This will tell you where the data is going missing.
Related
I have a WCF to save some data to my db. In Dev Tools I can see the request payload as below:
{"OrderId":123456,"Year":"2017","CustomerId":999999,"LicencePlates":[{"licenceNumber":"222222222222222"}]}
My WCF service interface is as below:
[OperationContract]
[WebInvoke(UriTemplate = "/SaveLicenceData", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
void SaveLicenceData(int OrderId, int Year, string CustomerId, LicencePlate[] LicencePlates);
The licence plate class is:
public class LicencePlate
{
[DataMember]
public string LicenceNumber { get; set; }
}
With a breakpoint set, I can see the OrderId getting passed, the Year getting passed and the CustomerId getting passed. However LicenceNumber I keep getting null passed.
I tried making the overall class [Serializable] but the breakpoint isn't even getting hit when I add this. I also tried changing the annotation too [DataMember(Name = "licenceNumber")] but still getting null and then changed the annotation to [JsonProperty] and still getting null.
Is there something stupid I have missed here?
I think you have a case mismatch. In your payload you have
"licenceNumber":"222222222222222"
It should be
"LicenceNumber":"222222222222222"
Trying to construct valid request to wcf restful service. But whole hour cannot do it, i dont get it why it's not work. Other APis what returns JSON works correctly, i tested them. Now need to fix request API.
I put break point to implementation of AddMeal method but she don't triggered. I think something wrong with my request or with Attributes
//Add Meal
[OperationContract]
[WebInvoke(UriTemplate = "AddMeal",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, Method = "POST")]
bool AddMeal(string meal);
Your WCF method is expecting a string, however, the model binder will try to bind your JSON to a string which won't work. When using MVC you should use models instead, create a model that represents your JSON data e.g.
public class Meal
{
public DateTime EatedTime { get; set; }
public decimal Amount { get; set; }
public int PatientID { get; set; }
public int MealTypeID { get; set; }
...
}
Then update your signature to expect that model
bool AddMeal(Meal meal)
You should find the data is bound to the meal model.
I have a WCF service that returns some JSON from a serialized object:
public class Response
{
public string Token { get; set; }
public int UserId { get; set; }
...
}
I've added some extra properties to this class, but now some of the implementations fails because they read it like:
string[] ResultLoginValues = e.Result.ToString().Split(',');
and it's returned in alphabetically order instead of the old order with the new properties last.
Is there any way I can change the order, or should they rewrite the clients?
For solution refer WCF DataContract DataMember order
I have a WCF web service that used to work fine. Somewhere down the line it stopped and I cant tell why. The code and the interface never changed nor did the web.config (at least not in relation to the web services section). I have a class:
[DataContract]
public class QuizServiceArgs
{
[DataMember(IsRequired = true, Order = 1)]
public int Category1 { get; set; }
[DataMember(IsRequired = true, Order = 2)]
public int Category2 { get; set; }
[DataMember(IsRequired = true, Order = 3)]
public int Category3 { get; set; }
[DataMember(IsRequired = true, Order = 4)]
public int Category4 { get; set; }
}
And the service interface is simple:
public interface IQuizService
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]
ServiceResult Save(QuizServiceArgs answers, string strvalue, int intvalue);
}
The second two params strvalue and intvalue were added only for troubleshooting to see if those were getting deserialized -- and they are. When I hit the service, I get an error saying that I'm missing the Category1 parameter from the request but as you can see this Fiddler screenshot, the values are there.
I can get primitive values to pass in but objects seem to all be instantiated with null or default values. What am I doing wrong?
UPDATE
I never actually got my original question answered which sucks, but Sixto suggested that I switch my serialization to JSON. JSON was the original design but got nixed when I was having trouble with it. After I successfully switched back to JSON, everything was serializing and deserializing properly. Now I am just waiting for this to break for no explanation so I can switch back to XML....
You have a namespace issue. By default, when you create an interface for the servicecontract, it assigns it a namespace. Namespace is like scope for the SOAP xml elements, and if it doesn't fall under the same scope, it thinks it doesn't exist. Chances are the code that posts stoppped providing the namespace(?). You have to refer by it when posting the XML - but I'm not entirely sure what it assigns(something server specific), so it is good practice to always define a namespace, like so:
[ServiceContract(Namespace = "your namespace")]
public interface IQuizService
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json)]
ServiceResult Save(QuizServiceArgs answers, string strvalue,
int intvalue);
}
And then the posts should have the namespace in the SOAP request.
<Save xmlns="your namespace">.....</Save>
The namespace should also match your service declaration in web.config.
Also need it in the datacontract
[DataContract(Namespace = "your namespace")]
The DataMember IsRequired attribute tells the WCF deserializer to expected an element in the message XML with that name (at least it does for soap deserialization). It sounds like the client code that is generating the message request is no longer sending all the Category... elements. It doesn't mean you can't send a null value for a DataMember marked IsRequired, it just means the element needs to exist in the message.
UPDATE: looking at the XML more closely, shouldn't there be a QuizServiceArgs element in the XML?
I'm trying to use ASMX/WCF to pass objects between sites (public / private). I can get the serialized object from my private ASMX service to my public WCF service, but I can't deserialize the object. Code below followed by error.
WCF service that calls a private ASMX service.
[WebGet(UriTemplate = "{part}")]
public Distributor GetDistributorInventory(string part)
{
const string url = "http://www.site.com/service/lookup.asmx/StockCheck?part=" + part;
//This is a wrapper for a HttpWebRequest that returns a string
string results = WebHelper.HttpRequest("GET", "text/xml", null, url, new CookieContainer());
byte[] byteArray = Encoding.ASCII.GetBytes(results);
MemoryStream stream = new MemoryStream(byteArray);
DataContractSerializer deserialize = new DataContractSerializer(typeof(Distributor));
return (Distributor)deserialize.ReadObject(stream);
}
Contract used in both Public/Private Services
[DataContract(Namespace = "http://www.site.com/Services/", Name = "Inventory")]
public class Inventory
{
[DataMember(Order = 1)]
public string MPN{ get; set; }
[DataMember(Order = 2)]
public string DataSheetURL { get; set; }
[DataMember(Order = 3)]
public List<Distributor> Stock { get; set; }
}
[DataContract(Namespace = "http://www.site.com/Services/", Name = "Distributor")]
public class Distributor
{
[DataMember(Order = 1)]
public string Name { get; set; }
[DataMember(Order = 2)]
public string Part { get; set; }
[DataMember(Order = 3)]
public int Quantity { get; set; }
[DataMember(Order = 4)]
public string URL { get; set; }
}
Error Message:
Error in line 1 position 166. Expecting element 'Distributor' from namespace 'http://www.site.com/Services/'.. Encountered 'Element' with name 'Inventory', namespace 'http://www.site.com/Services/'.
I might be going about this the entirely wrong way, so suggestions on a better approach (with sample) would greatly appreciate. My end goal is to pass objects between WCF & WCF or ASMX services using custom objects and DataContracts.
Looks like it's trying to deserialize as Distributor but the response from the StockCheck call is returning a Inventory.
The asmx service is probably not using the DataContractSerialiser. Here is short video on on how to do custom serialisation in a the ASMX service.
Or you could deserialise in the WCF service using the same serialiser that the asmx service is using (XmlSerialiser?)
Another option is to use the Json serialiser instead. There is an example here.
Here is the final solution I found for making this work, with the least amount of work possible.
I switched from passing my object as XML to JSON (was my final goal, tho I started with XML)
After getting my JSON object received, I noticed it had a wrapper "d:" and had a property "__type:" that was being added. Knowing these needed to be removed, I decided to find a generic way to remove the elements.
My generic solution was to use this code/article on Codeplex that had extension methods that did the clean-up using Reg Ex. Very simple.
Here is my resulting code:
[WebGet(UriTemplate = "{part}")]
public Distributor GetDistributorInventory(string part)
{
const string url = "http://www.site.com/service/lookup.asmx/StockCheck";
string results = WebHelper.HttpRequest("POST", "application/json; charset=utf-8", "{part: " + part + "}", url, new CookieContainer());
Inventory inventory = new JavaScriptSerializer().CleanAndDeserialize<Inventory>(results);
return inventory;
}
Another perk to this solution, is the ability to convert the object regardless of namespace. This results in being able to use the object "sender.Inventory" in my source ASMX service and the object "receiver.Inventory" in my WCF service where I'm going to consume the object.