How to include a class instance in a WCF data contract? - c#

I am trying to set up a WCF service to pass information about a complicated object, SimCalibrationData, to a client. SimCalibratonData contains a list of objects of type SimCalibrationBaseData. SimCalibrationBaseData has objects of type SimPlateData. All classes are decorated with [DataContract], and public properties are decorated with [DataMember]. If I wrap the SimPlateData objects into a List<>, I can send them to my client and they are successfully deserialized. If I don't wrap them, I get an error saying "The server did not provide a meaningful reply" and a suggestion that there might be a contract mismatch.
I've read that lists and other collections can always be serialized and deserialized, but I haven't found anything telling me what to do if I don't want to go to the trouble wrapping all my data in lists.
Here is my server-side code:
[DataContract]
public class SimCalibrationData
{
private List<SimBaseCalibrationData> m_baseCalibrations = new List<SimBaseCalibrationData>();
[DataMember]
public List<SimBaseCalibrationData> BaseCalibrations
{
get { return m_baseCalibrations; }
}
public SimCalibrationData(CSimThermalCalibrationList calibrationList)
{
foreach (CSimThermalCalibration calibration in calibrationList.Listing)
{
m_baseCalibrations.Add(new SimBaseCalibrationData(calibration));
}
}
}
[DataContract]
[KnownType(typeof(SimPlateData))]
public class SimBaseCalibrationData
{
// Plate Parameters
SimPlateData m_convectorPlate;
[DataMember]
public SimPlateData ConvectorPlate
{
get { return m_convectorPlate; }
}
SimPlateData m_loadPlate;
[DataMember]
public SimPlateData LoadPlate
{
get { return m_loadPlate; }
}
public SimBaseCalibrationData(CSimThermalCalibration calibration)
{
m_convectorPlate = new SimPlateData(calibration.ConvectorPlate);
m_loadPlate = new SimPlateData(calibration.LoadPlate);
}
}
[DataContract]
public class SimPlateData
{
public SimPlateData(CSimPlate plate)
{
}
}
Thank you for your help. In the meantime, I'll be wrapping everything in lists.

Tim's first comment, pointing out that serialized properties must have setters, was the answer.

Related

how to fix: System.Runtime.Serialization.InvalidDataContractException for arraylist

I'm writing a new DataContract to our service (which is in windows service and exposed by iis). in the main class i have an Arraylist member that when i expose i get System.Runtime.Serialization.InvalidDataContractException if i change the member to "List<>" then all is great (and it goes through from postman->iis->windows service).
the class is:
namespace Kitchen
{
[Serializable()]
[DataContract]
[KnownType(typeof(Fruit))]
[XmlInclude(typeof(Fruit))]
public class Foods
{
private ArrayList uniFood;
private List<string> lstCity;
[DataMember]
[XmlElement(typeof(Fruit))]
public ArrayList FoodArr
{
get
{
return uniFood;
}
set
{
uniFood = value;
}
}
[DataMember]
[XmlIgnore]
public List<string> CityGrown
{
get
{
return lstCity;
}
set
{
lstCity = value;
}
}
[DataMember]
[XmlIgnore]
public bool IsMerge
{
get;
set;
}
[DataMember]
[XmlIgnore]
public bool IsRipe
{
get;
set;
}
public Foods()
{
uniFood = new ArrayList();
lstCity = new List<string>();
}
}
}
the error i get when trying to send class to service reference:
System.Runtime.Serialization.InvalidDataContractException: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.
any help would be appreciated
This is a new feature in api, working on vs2013. in the past i've tried changing the arraylist to List<> and that helps but i need it as array list because when serializing to xml it comes out as:
Please use [CollectionDataContract(...)] attribute instead of [DataContract] attribute when Data Contract class has an collection member, please refer to the below document.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.collectiondatacontractattribute?redirectedfrom=MSDN&view=netframework-4.8

I want to fill class members with default if they are missing, using generic in C#

I have multiple web requests that post JSON object and I have serializable classes with all the fields. For example:
[Serializable]
public class RequestOne()
{
public string date;
public string information;
public string subject;
}
[Serializable]
public class RequestTwo()
{
public int ID;
public string Data;
public string message;
}
And my method takes partially filled request class and I want to fill in any missing fields with default values declared in constant class.
And I want to avoid writing each method with for each request, like :
public static void FillWithDefault(this RequestOne request)
{ if (request.date.Equals(null)) request.date = DEFAULT_DATE;
if (request.information.Equals(null)) request.information = DEFAULT_INFO;
if (request.subject.Equals(null)) request.subject = DEFAULT_SUBJECT;
}
public static void FillWithDefault(this RequestTwo request)
{
//do the same for the fields in RequestTwo
}
I want to know if there is any way to achieve this using generic?
I want to do something similar to this:
public static void FillWithDefault<T>(this T request)
{
if(typeof(T) == typeof(request))
{
//check each member in request and fill with default if it's null
}
.
.
.
}
So that in my main method I can use like this :
RequestOne request = new RequestOne();
request.FillWithDefault();
RequestTwo request2 = new RequestTwo();
request2.FillWithDefault();
Can someone please help with idea on this? Am I overthinking on this? I'm new to generic so please feel free to advise on my code.
Edit
Sorry guys, I did not mention that I will be using this method for test automation. Those request contracts cannot be changed since it's by design. Sorry again for the confusion!
Use constructors. Also make use of properties. Don't gather the default filling code to one place, it's the responsibility of the classes so keep them there.
[Serializable]
public class RequestOne()
{
public string date { get; set; };
public string information { get; set; };
public string subject { get; set; };
public RequestOne()
{
Date = DEFAULT_DATE;
Information = DEFAULT_DATE;
Subject = DEFAULT_SUBJECT;
}
}
[Serializable]
public class RequestTwo()
{
public int ID { get; set; };
public string Data { get; set; };
public string Message { get; set; };
public RequestTwo()
{
Data = DEFAULT_DATA;
message = DEFAULT_MESSAGE;
}
}
Generics are used when the types have common operations/properties defined so you can apply the same routine for each type in one place instead of declaring different methods for each type.
However in this case, you have two different types with different properties, so I would not use generics here. You can achieve it with manual type checking and using reflection to get properties and set them but it's not a good way and definitely wouldn't be a good usage of generics.
Overloading is the way to go.
you can use property
[Serializable]
public class RequestOne()
{
private string _date;
public string date { get { return _date;} set { _date = value ?? DEFAULT_DATE; }};
public string information; // same here
public string subject; //same here
}

JSON deserialization throws exception when nested object is empty

Hello I have a problem with deserializing JSON to object.
I have this kind of JSON:
{
"my_obj":{
"id":"test",
"nested_obj":{
"value":"testValue",
"desc":"testDesc"}
}
}
But sometimes I receive empty nested_obj:
{
"my_obj":{
"id":"test",
"nested_obj":""
}
}
My code to handle this:
public class MyObj
{
public string id { get; set; }
public NestedObj nested_obj { get; set; }
}
public class NestedObj
{
public string value { get; set; }
public string desc { get; set; }
}
public virtual T Execute<T>(IRestRequest request) where T : new()
{
request.RequestFormat = DataFormat.Json;
var response = _client.Execute<T>(request);
LogResponse(response);
CheckResponseException(response);
return response.Data;
}
When the nested_obj is not empty, then deserialization works perfectly fine. But when nested_obj is empty, I receive this exception:
Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
Is it possible to deserialize it this way? Unfortunately it is not possible to change response of the WebService, so i should parse it correctly in my app.
Simply use Newtonsoft's Json.NET. It works fine. Here's a short snippet from LINQPad:
void Main()
{
JsonConvert.DeserializeObject<A>(#"{
property: ""apweiojfwoeif"",
nested: {
prop1: ""wpeifwjefoiwj"",
prop2: ""9ghps89e4aupw3r""
}
}").Dump();
JsonConvert.DeserializeObject<A>(#"{
property: ""apweiojfwoeif"",
nested: """"
}").Dump();
}
// Define other methods and classes here
class A {
public string Property {get;set;}
public B Nested {get;set;}
}
class B {
public string Prop1 {get;set;}
public string Prop2 {get;set;}
}
And the result is
Ouch. That has nothing to do with your code as you already know. It's just that the web service is sometimes defining nested_obj as a NestedObj object, and sometimes as a string (when it sends null). So your parser doesn't know what to make of it.
Your best bet might be to write a custom parser for it. (which makes sense since it's not standard JSON due to the type morphing).
There's a section on writing a custom parser for RestSharp here
They tell you to implement IDeserializer but I'd suggest you simply extend JsonDeserializer to add your special custom functionality and delegate back to the super class for everything else.
Reason is because in
{
"my_obj":{
"id":"test",
"nested_obj":{
"value":"testValue",
"desc":"testDesc"}
}
}
nested object is receiving an object type
while in
{
"my_obj":{
"id":"test",
"nested_obj":""
}
}
it is receiving string type.
if
it returns
{
"my_obj":{
"id":"test",
"nested_obj":null
}
}
then it will be able to parse successfully.
try using http://json2csharp.com/ to convert both of your json and see the difference

Protobuf-net deserializing after defining inheritance

I'm using protobuf-net version 2.0.0.640 to serialize some data as shown below.
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public interface ITestMessage
{
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class MyOrder : ITestMessage
{
public int Amount { get; set; }
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class MyOrderWrapper
{
public MyOrder Order { get; set; }
}
[TestMethod]
public void TestOrderSerialize()
{
var order = new MyOrder() {Amount = 10};
var orderWrapper = new MyOrderWrapper() { Order = order };
using (var file = File.Create("C:\\temp\\order.bin"))
{
Serializer.Serialize<MyOrderWrapper>(file, orderWrapper);
}
}
Now, If I declare an inheritance dependency between 'ITestMessage' & 'MyOrder' via code using:
RuntimeTypeModel.Default[typeof(ITestMessage)].AddSubType(2, typeof(MyOrder));
I get the following error when trying to deserialize my prevously saved data.
"No parameterless constructor found for ITestMessage".
[TestMethod]
public void TestOrderDeserialize()
{
RuntimeTypeModel.Default[typeof(ITestMessage)].AddSubType(2, typeof(MyOrder));
MyOrderWrapper orderWrapper;
using (var file = File.OpenRead("C:\\temp\\order.bin"))
{
orderWrapper = Serializer.Deserialize<MyOrderWrapper>(file);
}
}
Can someone please explain why this would happen when 'MyOrderWrapper' does not reference anything higher than 'MyOrder' in the inheritance hirarchy.
Also, why it works when I explictly include '[ProtoInclude(2, typeof(MyOrder))]' on 'ITestMessage'
Thanks
Basically, that is a breaking change as far as the serializer is concerned - at the wire layer, neither "classes" nor "interfaces" exist, so in terms of storage, this is akin to changing the base-type of the class; when serializing, the root type was MyOrder - and during deserialization the root type is ITestMessage. This will not make it happy.
Basically: you can't do that.

How to serialize a class with a property of type object filled with an array

After searching 99% of the net I am still stuck on the following matter. I have a web service which must comply to a wsdl that a partner company supplied. Calling a method of this service results in a (complex) class. Unfortunately a serialization error is raised when the service is called.
I have pinpointed the issue but cannot think of (and find) a solution to it. Because I'm dependant on the wsdl which was supplied, I cannot change the complex class structure. Hope anyone knows what I am missing. Here is example code to reproduce my issue:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
public object Item { get; set; } // <---- Note type *object* here
}
[System.SerializableAttribute()]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void Serializing()
{
MyObject myObject = new MyObject { Id = 1 };
myObject.Item = new MyItem[] { new MyItem { Id = 1, Name = "Test" } };
string serializedString = SerializeObjectToXmlString(myObject, new []{ typeof(MyItem)});
Assert.IsFalse(String.IsNullOrWhiteSpace(serializedString));
}
/// <summary>
/// This method serializes objects to an XML string using the XmlSerializer
/// </summary>
private static string SerializeObjectToXmlString(object theObject, Type[] types)
{
using (var oStream = new System.IO.MemoryStream())
{
var oSerializer = new System.Xml.Serialization.XmlSerializer(theObject.GetType(), types);
oSerializer.Serialize(oStream, theObject); // <- Here the error is raised
return System.Text.Encoding.Default.GetString(oStream.ToArray());
}
}
}
In the Try/Catch an error is raised after calling method Serialize(). Details of this error are:
InvalidOperationException was unhandled by user code
- There was an error generating the XML document.
The type MyItem[] may not be used in this context.
My development context consists of Visual Studio 2010, .Net Framework 3.5.
Edit #1: Added Serialization attributes but the error remaines
It appears that you cannot add an array of types to an object and serialize it. The solution was to create a container class which - like the name says - contains the array. This way you can assign the container class to the object and serialize it all.
In addition to my case, I was mislead by the object model created by the wsdl.exe utility, since the container class is only a technical solution to add an array to an object. This container class was also created so everything was already there to use. Only after trying out my custom container class I noticed the already created container class. Lost a lot of time on this matter unfortunately...
You should mark you classes as
[Serializable]
public class MyObject
{
public int Id { get; set; }
public MyItem[] Item { get; set; } // <---- Note type *object* here
}
[Serializable]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Serialize uknown object (Item of MyObject class) you will need to do manually by implementing proper interfaces:
ISerializable and IDeserializationCallback, botha added to MyObject class.
This is an old question, but I had the same problem and found a different solution, so I thought I'd share in case it helps someone else.
I found that I could add attributes to allow arrays of specific types. For the problem above, the MyObject class could be edited as below:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
[XmlElement(Type = typeof(object), ElementName = "Item"), //added
XmlElement(Type = typeof(MyItem[]), ElementName = "Item_asArrayOfMyItem")] //added
public object Item { get; set; } // <---- Note type *object* here
}
Anything that serialized before will still look the same, but now MyObject can be serialized even when Item has type MyItem[], as in the question's test case.

Categories