Deserialize matching types from different assemblies - c#

If I have a class/interface pair defined in AssemblyA under namespace AssemblyA.Entities:
public IEntity
{
string Name { get; set; }
}
[Serializable]
public Entity : IEntity
{
public string Name { get; set; }
}
And I serialize it to XML using XmlSerializer:
var myEntities = new List<IEntity>();
// myEntities is populated somehow
var data = XmlSerializationManager.Serialize(myEntities);
// 'data' gets saved to disk somewhere as a file
Then, if I duplicate the code/namespaces into AssemblyB so that I have namespace AssemblyA.Entities and the exact same code:
public IEntity
{
string Name { get; set; }
}
[Serializable]
public Entity : IEntity
{
public string Name { get; set; }
}
If I attempt to deserialize the previously serialized XML, will I get back AssemblyB's list of AssemblyA.Entities.IEntity? Or will it fail?
At what point does the serializer stop caring about what it's deserializing to? Can the assembly differ? Can the namespaces differ? Do the type names matter so long as the properties are named the same?

This will work. You will get AssembyB entity.
This is essentially how Web services work when the client would scaffold classes based on information in the wsdl, the client would then deserialise the data from the soap message into these scaffolded classes.

Related

How to deserialize complex json response in poco using restsharp when model classes are defined separately for each section

Hi I am new to RestSharp and C#. I am trying to deserialize a complex JSON response into a POCO using RestResponse<T> from RestSharp. The issue is that the response has a nested structure like Dictionary<List<Dictionary<key,object>>> but the model classes are defined separately, which means for List objects there is a separate class and for the overall Dictionary there is no class or object defined.
How can I deserialize this?
// Root.cs
using System;
using System.Collections.Generic;
namespace MStestPracticeProject.Models.ModelsForGetPracticeDirectory
{
public class Root
{
public List<Workspace> workspaces { get; set; }
}
}
// Workspace.cs
using System;
namespace MStestPracticeProject.Models.ModelsForGetPracticeDirectory
{
public class Workspace
{
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
public string visibility { get; set; }
}
}
In testClass I am trying the following which doesn't work because RestResponse<T> needs a proper POCO class.
RestResponse<Dictionary<"workspace", List<Root>>> restResponse =
restClient.ExecuteGet<Dictionary<"workspace", List <Root>>>(restRequest);
How should I approach this type of deserialization?

Use DataMember during deserialize but nut during serialize?

I'm having trouble understanding serialization of one of my objects.
Scenario:
I'm receiving data from a service which is in a given format. I want to take the data in as is. Due to naming conventions I have to use DataMember properties to match the incoming data to properly named class properties. I use System.Runtime.Serialization for this. Example:
[DataContract]
public class IncomingData
{
[DataMember(Name = "$Filename")]
public string Filename { get; set; }
}
This works fine and the data is mapped to the internal property name.
At some point I have to serialize this object again and I naively thought that it would serialize to the internal property name e.g.
{ "Filename":"C:\temp\lala.txt"}
however that is not true and the original propertyname "$Filename" is used instead. I assume this is because DataMember works both ways.
Is there an elegant way to have this object serialize to the propertynames and ignore DataMember? I tried if using a different serialization library works (JSON.NET) but it also seems to follow DataMember.
Do I have to wrap this object to another to acchieve?
Thanks for any hints!
blu
You could define an interface for keeping these objects in sync...
public interface IData
{
string Filename { get; set; }
}
// deserialize me.
[DataContract]
public class IncomingData : IData
{
[DataMember(Name = "$Filename")]
public string Filename { get; set; }
}
// serialize me.
public class Data : IData
{
public string Filename { get; set; }
}
...or you could use virtual properties and override them with the serialization attributes...
// serialize me.
class Data
{
public virtual string Filename { get; set; }
}
// deserialize me.
[DataContract]
class IncomingData : Data
{
[DataMember(Name = "$Filename")]
public override string Filename { get => base.Filename; set => base.Filename = value; }
}
...both of these methods would require the use of a mapper like AutoMapper to clone the IncomingData into the attribute-free Data class...
mapper.Map<IncomingData, Data>(user);
...so I appreciate this feels less than ideal.

exposing datacontract defined in a different namespace via WCF

I have developed a webservice, it accepts object as input
namespace Someservice
{
public bool CreateBehavior(ClassInput classInput)
{
// code here
}
}
now I have defined this ClassInput as a datacontract in business entities which is a different namespace
namespace Someservice.BusinessEntities
{
[DataContract]
public class ClassInput
{
[DataMember]
public int intvariable{ get; set; }
[DataMember]
public string stringVariable { get; set; }
}
}
To use this ClassInput in the webservice i have added project reference to Business entities project.
Now how can i expose this datacontract to consumer of this webservice?
For instance in client side, they will need this classInput, but its not exported via service!!
SomeserviceClient someserviceClient = new SomeserviceClient();
someserviceClient.CreateBehavior(classInput)
I read about datacontractsurrogate but i am not able to map that to this scenario

C# - WCF - Generics - [KnownType(typeof(xxx))]

My client has 10 tables that it needs to load via an internal WCF to a server. Since all this is internal, I can write both client and server using whatever technique i want.
On the Client, I thought to use LINQ to load data from the tables to a List, List and so on...
On the Server, I thought to have a [DataContract] as follow:
[DataContract]
[KnownType(typeof(Table1))]
[KnownType(typeof(Table2))]
[KnownType(typeof(Table3))]
public class GenericType<T>
{
[DataMember]
public List<T> Data { get; set; }
}
and then add the classes that will represent the matching Tables on the Client.
[DataContract]
public class Table1
{
[DataMember]
public int UserID { get; set; }
[DataMember]
public string FullName { get; set; }
}
[DataContract]
public class Table2
{
[DataMember]
public int UserID { get; set; }
[DataMember]
public string Address1 { get; set; }
}
[DataContract]
public class Table3
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
}
When I create the client reference, i'm NOT getting all the classes declared on the server and it seems that ONLY the 1st [KnownType] specified on the [DataContract] becomes visible to the Client.
I was under the impression that Generics was meant to allow multiple types but am I right to think that WCF can only handle one [KnownType] x class ??
And if so, my only way to code this would be to copy and paste 10 times the GenericType class and on each copy, change the [KnownType] ??
Cause if that's the only solution, then what are the real benefits to use Generic instead of straight defined List, List for my params ??
Any thought will help clarify my mind here
The problem happens because unless ONE of the WCF methods uses any of the CLASSES declared as [DataContract] ...it seems that WCF does NOT brings those classes to the Client.
Is this the expected case?
You could try attributing your interface method with the ServiceKnownType attribute for each of the classes.
There is another option, which is to implement the generic lists in classes that are attributed with CollectionDataContract:
[CollectionDataContract]
public class Table1Collection
Inherits List<Table1>
On the client side, you can the edit Reference.svcmap and enumerate each of the collections in the CollectionMappings section:
<CollectionMappings>
<CollectionMapping TypeName="My.Namespace.Table1Collection" Category="List" />
This allows you to reuse the same code on both ends of the pipe.

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