c# How to specify custom Interface for Serialization - c#

i got a Class Library myCore.dll
it includes a class
public interface IMyClassA {
public string A { get; set; }
public string B { get; set; }
}
public class MyClassA: IMyClassA {
public string A { get; set; }
public string B { get; set; }
}
then i got a console project with a selfhosted http service. it references myCode.dll and does some interfacing with json via http.
but i want to hide Member 'B' of MyClassA if i do serialization in this project.
im using Newtonsoft.Json. But i dont want to reference Newtonsoft in myCode.dll to set the [JsonIgnore] Attribute on MyClassA.B.
so how do i create a custom interface in my console-project that inherits from IMyClassA?

You have to write a custom converter.
If you use NewtonSoft you will have two methods to override: ReadJson(...) and WriteJson(...), one is for serializing the other for deserializing. That way you can write you own code responsible for serializing and deserializing.
Having your own code you can just ignore the member 'B' of MyClassA.
To register the converter, instead of using an anotation on your DTO
[JsonConverter(typeof(MyCustomConvreter))]
public interface IMyClassA {
public string A { get; set; }
public string B { get; set; }
}
which would lead to an undesired reference to Newtonsoft, you can do this to register the custom converter:
var jsonSerializer = new JsonSerializer();
jsonSerializer.Converters.Add(new MyCustomConverter());
Check NewtonSoft docs for a custom converter: https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm

Related

Base Class For JSON Data

I'm creating objects to store the JSON data I will be receiving and cannot figure out the right way to structure the objects. Basically, I can be receiving two different objects that only have differences in the body, so I wish to make a base class.
public class SampleBase
{
public string url { get; set; }
public string resource { get; set; }
public string Body body { get; set; }
}
This is an example of the base, with the Body object declared below
public abstract class Body{ }
I then have two separate files for the versions of the base object I can receive, with an example below:
public class SampleObject : SampleBase
{
public class Body
{
public string bodyproperty { get; set; }
}
}
I am doing this just to be efficient with the classes since they share some properties. The SampleBase class will never be called, instead incoming json will be deserialized into SampleObject. Is this best practice?
Edit: Going by this example, the JSON is received as
{
"url": "xxxxxxxxxx",
"resource": "xxxxxxx",
"body": {
"bodyproperty": "xxxx",
}
}
Your class structure can heavily depend on your choice of serializer. For example, the DataContractJsonSerializer can technically handle inherited classes, but it does it in somewhat of a clunky way. You need to define all the known inheritors of your base type on the base type.
Personally, I would use composition rather than inheritance in your case. Here's an example using the DataContractJsonSerializer:
[DataContract]
public class Wrapper<T> where T : Body
{
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "resource")]
public string Resource { get; set; }
[DataMember(Name = "body")]
public string T Body { get; set; }
}
[DataContract]
public class Body
{
[DataMember(Name = "bodyproperty")]
public string BodyProperty { get; set; }
}
Then you'd use the class like any other generic.
Wrapper<Body> obj = new Wrapper<Body>();
Edit: Since this is a MVC application, you'll likely be working with the JavascriptSerializer. The DataContract and DataMember can be ignored but the structure of the classes is still relevant.
var serializer = new JavaScriptSerializer();
var data = serializer.Deserialize<Wrapper<Body>>(json);

Which must be used with XmlSerializer and WCF - DataContract or MessageContract?

I need to write WCF service wich will take .xml file deserialize it into my custom calss and then do some operations on that data. I have written test version of this application which use console but not WCF. Here is how my code looks like:
[Serializable]
public class ErrorMsgElement
{
[XmlElement("Sender")]
public string SenderOfMessage{ get; set; }
[XmlElement()]
public Int64 UserID { get; set; }
[XmlElement()]
public Int64 SerialNumber { get; set; }
[XmlElement("DateTime")]
public DateTime DateAndTimeOfMessage { get; set; }
}
[Serializable]
[XmlRoot("Root")]
public class ErrorMessage
{
[XmlElement("Header")]
public string HeaderOfFile { get; set; }
[XmlElement("ErrorMsg")]
public ErrorMsgElement msgElent { get; set; }
}
And Main code
ErrorMessage myErrorMsg = new ErrorMessage();
//pathToFile is original path
string path = #pathToFile;
XmlSerializer myXmlSerializer = new XmlSerializer(typeof(ErrorMessage));
using (StreamReader myStrReader = new StreamReader(path))
{
myErrorMsg = (ErrorMessage)myXmlSerializer.Deserialize(myStrReader);
}
//some operations on myErrorMsg such as writing to database
It works fine in console application. Now I need to write same logic in WCF service. But I don't know what should I use - [Serializable], [DataContact], [MessageContract] or someting else?. Consider that I have some [XmlArray] atributes in my custom classes as well. And if I add [DataContract] atribute to my ErrorMessage class do I have to add same attribute to ErrorMsgElement as following?
[DataContract]
[Serializable]
public class ErrorMsgElement
{
...
}
[DataContract]
[Serializable]
[XmlRoot("Root")]
public class ErrorMessage
{
...
[DataMember]
[XmlElement("ErrorMsg")]
public ErrorMsgElement msgElent { get; set; }
}
All answers are well appreciated. Thanks in advance.
in wcf [DataContact] and DataContractSerializer are recommended for what do you want to do. But you can also use XmlSerializer as well it's really only your choice(using first or second don't making diffrent for performance of your application).
Edit:
When you adding DataContract attribute to your class than you don't have to add Serializable attribute too the same class DataContract is equal to Serializable
check this for answer about nested DataContracts

How do I remap names to object properties when deserializing, using Json.Net in C#

Case:
I receive a JSON string from third-party server, containing a list of objects.
e.g.
[{"foo": "bar", "someotherfield": "somevalue"}, {etc}, {etc}]
I have a C# class like,
public class MyObject
{
public string A { get; set; }
public string B { get; set; }
}
Now, I want to use JsonConvert.DeserializeObject, but so that it maps "foo" to A and so forth. How would I go about doing this?
You can use the JsonProperty attribute.
[JsonProperty("foo")]
public string A { get; set; }
You can use the JsonPropertyAttribute decorated on the property, giving it the name of the parameter it should find in the JSON message. You can also use a custom JsonConverter, if you do not wish to use an attribute.

ServiceStack.Text JSON parsing on .Net 4.0

H chaps,
I am trying to use ServiceStack.Text for JSON parsing (it seems to be performing better than JSON.Net in various benchmarks I have seen). But I am not getting the results I am expecting. The class I am attempting to deserialize looks like this:
[DataContract]
public class RpcRequest<T>
{
[JsonProperty("id")]
[DataMember(Name="id")]
public String Id;
[JsonProperty("method")]
[DataMember(Name="method")]
public String Method;
[JsonProperty("params")]
[DataMember(Name="params")]
public T Params;
[JsonIgnore]
[IgnoreDataMember]
public Policy Policy;
}
And I am invoking the parser like this
public static class Json
{
public static T Deserialize<T>(string serialized)
{
return TypeSerializer.DeserializeFromString<T>(serialized);
}
}
...
RpcRequest<Params> myRequeset = Json.Deserialize(packet);
However I am getting an instance back from that call which has none of the values set. ie Id, Method and Params are all null. Am I using this API correctly?
It seems that ServiceStack does not support public fields, only public properties. So if I change my model object to the following it all works.
[DataContract]
public class RpcRequest<T>
{
[JsonProperty("id")]
[DataMember(Name="id")]
public String Id { get; set; }
[JsonProperty("method")]
[DataMember(Name="method")]
public String Method { get; set; }
[JsonProperty("params")]
[DataMember(Name="params")]
public T Params { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public Policy Policy { get; set; }
}
Note the addition of getters and setters to each property.
I think you want JsonSerializer instead of TypeSerializer.
TypeSerializer is a new-fangled JSV format that Mr Mythz details on his blog here: http://www.servicestack.net/mythz_blog/?p=176

Why does the proxy generated code create a new class when a MessageContract is in my WCF Service?

I have created two WCF Services (Shipping & PDFGenerator). They both, along with my ClientApp, share an assembly named Kyle.Common.Contracts. Within this assembly, I have three classes:
namespace Kyle.Common.Contracts
{
[MessageContract]
public class PDFResponse
{
[MessageHeader]
public string fileName { get; set; }
[MessageBodyMember]
public System.IO.Stream fileStream { get; set; }
}
[MessageContract]
public class PDFRequest
{
[MessageHeader]
public Enums.PDFDocumentNameEnum docType { get; set; }
[MessageHeader]
public int? pk { get; set; }
[MessageHeader]
public string[] emailAddress { get; set; }
[MessageBodyMember]
public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
}
[DataContract(Name = "TrackResult", Namespace = "http://kyle")]
public class TrackResult
{
[DataMember]
public int SeqNum { get; set; }
[DataMember]
public int ShipmentID { get; set; }
[DataMember]
public string StoreNum { get; set; }
}
}
My PDFGenerator ServiceContract looks like:
namespace Kyle.WCF.PDFDocs
{
[ServiceContract(Namespace="http://kyle")]
public interface IPDFDocsService
{
[OperationContract]
PDFResponse GeneratePDF(PDFRequest request);
[OperationContract]
void GeneratePDFAsync(Kyle.Common.Contracts.Enums.PDFDocumentNameEnum docType, int? pk, string[] emailAddress);
[OperationContract]
Kyle.Common.Contracts.TrackResult[] Test();
}
}
If I comment out the GeneratePDF stub, the proxy generated by VS2010 realizes that Test returns an array of Kyle.Common.Contracts.TrackResult. However, if I leave GeneratePDF there, the proxy refuses to use Kyle.Common.Contracts.TrackResult, and instead creates a new class, ClientApp.PDFDocServices.TrackResult, and uses that as the return type of Test.
Is there a way to force the proxy generator to use Kyle.Common.Contracts.TrackResult whenever I use a MessageContract? Perhaps there's a better method for using a Stream and File Name as return types?
I just don't want to have to create a Copy method to copy from ClientApp.PDFDocServices.TrackResult to Kyle.Common.Contracts.TrackResult, since they should be the exact same class.
After a lot of extra digging, I realize that it was actually the Enum that "broke" it. It has do with the way DataContractSerializer works vs. XmlSerializer. Long story short, the solution was to turn the Enum into a nullable.
[MessageContract]
public class PDFRequest
{
[MessageHeader]
public Enums.PDFDocumentNameEnum? docType { get; set; }
[MessageHeader]
public int? pk { get; set; }
[MessageHeader]
public string[] emailAddress { get; set; }
[MessageBodyMember]
public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
}
I ran into the same problem (MessageContract+enums) and your post helped me. Indeed if you explicitly set the enum fields to nullable it works. The issue is that when enums are used, WCF uses the XML serializer which cannot tell null from empty string.
There is a detailed explanation of this behaviour here by one of the actual WCF team members.
In the case of document/literal when using bare messages, WCF falls back to XmlSerializer when handling enum types. ... XmlSerializer treats null as missing by default ... we encounter a schema without nillable="true" ... The detection logic for value types currently only handles primitive value types, it does not check for enums.
In other words WCF does not like enums... But hey, it works, you just need to be aware of it!
You can instruct Visual Studio to re-use classes from referenced assemblies. So if your test project has an assembly reference to the Kyle.Common.Contracts assembly, it should re-use those types defined in there rather than adding new client-side proxy classes.
The switch to enable this is on the Advanced page in the Add Service Reference dialog window - it should be on by default:
Make sure that your project
has an assembly reference to the common data contract assembly
that this setting is really ON when you add the service reference

Categories

Resources