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

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

Related

c# How to specify custom Interface for Serialization

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

WCF Generic result class always is empty

I have this generic class for my server responses:
[DataContract]
public class GenericResult<T>
{
public List<T> ListResult { get; set; }
public T Result { get; set; }
public string Message { get; set; }
}
and this GetAllBrandsTest method to return data to the client:
public async Task<GenericResult<Brand>> GetAllBrandsTest()
{
var result = await repo.GetAllAsync<Brand>();
return new GenericResult<Brand>()
{
ListResult = result.ToList(),
Message = "Success"
};
}
Everything is OK with this methods counterpart GetAllBrands:
public async Task<IList<Brand>> GetAllBrands()
{
return await repo.GetAllAsync<Brand>();
}
But when I call GetAllBrandsTest the result is empty.
[DataContract]
[KnownType(typeof(Brand))]
public class GenericResult<T>
{
[DataMember]
public List<Brand> ListResult { get; set; }
[DataMember]
public Brand Result { get; set; }
[DataMember]
public string Message { get; set; }
}
Any data type transferred between the server-side and the client-side should be explicitly specified how we serialize and deserialize it. Please use the DataContract attribute to specify the data structure the way how to serialize to XML so that the serialization and deserialization can work properly between the service-side and client-side. In addition, for unknown data types, please use the KnownType feature to specify the serialization method in advance.
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-known-types
We either decorate the class with both DataContract attribute and DataMember attribute or remove the DataContract attribute and DataMember attribute. Because the DataContract serializer will be used by default when the complex data type is without specifying any XML serializer.

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);

XML serialization - bypass container class?

I am trying to serialize an object in a particular manner. The main class has a container class that holds some attributes, but really these attributes should be on the main class, from the point of view of the schema. Is there a way to bypass the container class and treat the properties on the container class as properties on the main class, for the purposes of serialization?
I am trying to create XML along the lines of:
<Main foo="3" bar="something">
<Others>etc</Others>
</Main>
from this code:
[System.Xml.Serialization.XmlRootAttribute("Main", Namespace = "")]
public class MainObject
{
public HelperContainer { get; set; }
public string Others { get; set; }
}
public class HelperContainer
{
[System.Xml.Serialization.XmlAttributeAttribute(AttributeName = "foo")]
public int Foo { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute(AttributeName = "bar")]
public string Bar { get; set; }
}
You may want to try implementing IXmlSerializable on MainObject to be able to control what happens when calling serialize. For the read and write xml methods specify the fields to serialize.
Check out msdn: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
Something like:
public class MainObject : IXmlSerializable
{
public HelperContainer { get; set; }
public string Others { get; set; }
public void WriteXml (XmlWriter writer)
{
writer.WriteString(Others);
writer.WriteAttributeString("foo", HelperContainer.Foo);
writer.WriteAttributeString("bar", HelperContainer.Bar);
}
public void ReadXml (XmlReader reader)
{
Others = reader.ReadString();
//...
}
}

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