ASP.net Web API: change class name when serializing - c#

I have a Data Transfer Object class for a product
public class ProductDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
// Other properties
}
When the Asp.net serializes the object in JSON (using JSON.NET) or in XML, it generates ProductDTO objects.
However, i want to change the name during serialization, from ProductDTO to Product, using some kind of attributes:
[Name("Product")]
public class ProductDTO
{
[Name("ProductId")]
public Guid Id { get; set; }
public string Name { get; set; }
// Other properties
}
How can i do this?

I can't see why the class name should make it into the JSON-serialized data, but as regards XML you should be able to control the type name via DataContractAttribute, specifically via the Name property:
using System.Runtime.Serialization;
[DataContract(Name = "Product")]
public class ProductDTO
{
[DataMember(Name="ProductId")]
public Guid Id { get; set; }
[DataMember]
public string Name { get; set; }
// Other properties
}
DataContractAttribute is relevant because the default XML serializer with ASP.NET Web API is DataContractSerializer. DataContractSerializer is configured through DataContractAttribute applied to serialized classes and DataMemberAttribute applied to serialized class members.

An option is to use the default .Net Serialization attributes for this:
[DataContract(Name = "Product")]
public class ProductDTO
{
[DataMember(Name = "ProductId")]
public Guid Id { get; set; }
[DataMember]
public string Name { get; set; }
// Other properties
}

Related

Deserialize Json for CRM Entities

I have the following Json defining a specific configuration which has to be stored in custom CRM entities:
{
"useraccountid": "U12345",
"profiles": [
{
"applicationrole": "RelationshipManager",
"maindatascopetypecd": 858000001,
"organisationalunitno": "10000000",
"ishierachical": 1
},
{
"applicationrole": "CountrySpecialist",
"maindatascopetypecd": 858000002,
"attributetypecd": 858000000,
"attributevalue": "SY",
"isreadonly": 0
}
]
}
Each user can have multiple user provisioning profiles. this data finally needs to be written into custom CRM entities. The "useraccountid" is a lookup to a systemuser (entityreference).
What I already have is e deserializer like this:
public static T JsonDeserialize<T>(string jsonString)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
T obj = (T)ser.ReadObject(ms);
return obj;
}
And classes like this for the parent class:
[DataContract]
class CrmUserProvisioning
{
[DataMember]
public String clm_useraccountid
{
get; set;
}
// will be set on runtime
public DateTime clm_createdon
{
get; set;
}
[DataMember]
public List<CrmProfile> clm_profiles { get; set; } = new List<CrmProfile>();
}
and for the profile(s)
[DataContract]
public class CrmProfile
{
[DataMember]
public Guid clm_userprovisioningid
{
get; set;
}
[DataMember]
public string clm_applicationrole
{
get; set;
}
[DataMember]
public int clm_maindatascopetypecd
{
get; set;
}
[DataMember]
public string clm_organisationalunitno
{
get; set;
}
[DataMember]
public bool clm_ishierachical
{
get; set;
}
[DataMember]
public int clm_applyingtypecd
{
get; set;
}
[DataMember]
public int clm_globalopenaccesscd
{
get; set;
}
[DataMember]
public int clm_attributetypecd
{
get; set;
}
[DataMember]
public string clm_attributevalue
{
get; set;
}
[DataMember]
public bool clm_isreadonly
{
get; set;
}
}
missing fields in the config for the profile will not be deserialized. I'm stuck with the fact on how deserialize this config regarding the fact that the config can contain many profile classes but only one parent class (UserProvisioning).
Could anybody put me in the right direction? Any help is really appreciated.
kind regards
UPDATE 02.03.2018
After having deserialized the Jsion into object(s) I need to store now the objects into the corresponding Microsoft Dynamics CRM entites. The entities and the attributes are named like the object- and the property-names.
I already have the organisation service, etc. I only need to know how to map the objects to a regular crm create or update request.
If somebody could help me out with this, it would be very appreciated.
Use DataContract and DataMember
[DataContract]
class UserProvisioning
{
[DataMember]
public String useraccountid
{
get { return this.useraccountid; }
set { this.useraccountid = value; }
}
// Will be set on runtime
public DateTime createdon
{
get { return this.createdon; }
set { this.createdon = value; }
}
// Must declare this for the child list of Profile
[DataMember]
public List<Profile> profiles {get;set;}=new List<Profile>();
}
In CrmProfile also set DataContract for the class and DataMember for the properties.
Check the docs:
DataContract
DataMember. Only the properties with DataMember will serialize.
You can also set required properties or set not to serialize default values.

Prevent class property from being serialized

I added attribute serializable to class but, due to this, class property is getting serialized.
I used [XmlIgnore] to all property but still it is serializing the property
[Serializable]
public class Document
{
[DataMember]
[XmlIgnore]
public string FileURL { get; set; }
[DataMember]
[XmlIgnore]
public string FileSize { get; set; }
}
It's serialized like below tag-
<a:_x003C_DocumentDetails_x003E_k__BackingField>
<a:Document>
<a:_x003C_FileType_x003E_k__BackingField>PDF</a:_x003C_FileType_x003E_k__BackingField>
<a:_x003C_FileURL_x003E_k__BackingField>C:/log/Test.pdf</a:_x003C_FileURL_x003E_k__BackingField>
</a:Document>
</a:_x003C_DocumentDetails_x003E_k__BackingField>
If you are using the [Serializable] attribute, you need to use the [NonSerialized] attribute on any members (public or private) that you don't want serialised.
[DataMember] is used when the class is marked with the [DataContract] attribute and [XmlIgnore] is used when you are explicitly using the XmlSerialiser on a class.
[Serializable]
public class Document {
[NonSerialized]
public string FileURL { get; set; }
[NonSerialized]
public string FileSize { get; set; }
}
try [JsonIgnore] or [IgnoreDataMember] attribute, that will help you.
If you're using WCF with an "out of the box" configuration, you're probably using the DataContractSerializer to serialize messages, not the XmlSerializer.
In order to have members of your contract class not be serialized, you decorate them with the IgnoredDataMember attribute:
[Serializable]
public class Document
{
[DataMember]
public string FileURL { get; set; }
[IgnoredDataMember]
public string FileSize { get; set; }
}
Have you tried IgnoreDataMemberAttribute as per the docs?
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ignoredatamemberattribute.aspx

Dynamic/Expando and JSON

There's a lot of Qs on this, but I need a solution without JSON.Net, etc. - I must use the canned stuff in Asp.Net MVC.
How can I serialize a POCO with a dynamic property - and get all the static properties, too? What I found was the dynamic only, or the static type which is easy.
e.g.
public class ReturnThisClassAsJSON {
public int Id {get; set; }
public string Name { get; set; }
public ContainedClass ContainedContents { get; set; }
}
public class ContainedClass {
public int Order { get; set; }
public string Label { get; set; }
public dynamic DynamicInfo { get; set; }
public List<dynamic> DynamicList { get; set }
}
My own answer:
I replaced the dynamic from the DynamicInfo and DynamicList from the ContainedClass with static types.
With the dynamic, I had 1 of 2 choices. Either serialize the dynamic to a string in its own serialization call using above SO question 5156664. (Which left me with the rest of the class I also wanted serialized and merged with it, thus this question). Or, incur this error:
"A circular reference was detected while serializing an object of type 'System.Reflection .RuntimeModule' ".
when attempting a single serialization call on the ContainedClass.
So, I transferred the dynamics into static-typed classes:
public class ColumnValue
{
public string Name { get; set; }
public string Value { get; set; }
}
public class DynamicRow
{
public List<ColumnValue> ColumnValue { get; set; }
}
and, change ContainedClass to this:
public class ContainedClass
{
public List<ColumnValue> DynamicInfo { get; set; }
public List<DynamicRow> Data { get; set; }
}
And, it serializes using out-of-the-box Asp.Net MVC:
return Json(ReturnThisClassAsJSON, JsonRequestBehaviour.AllowGet);

Why am I getting the exception "Consider using a DataContractResolver or add any types not known statically to the list of known types"

I'm trying to serialise a object to Xml using the DataContractSerializer. I have the following classes;
[ActiveRecord(Lazy = true)]
[KnownType(typeof(RoomType))]
[DataContract]
public class Room : ActiveRecordBase<Room>
{
[PrimaryKey]
[DataMember]
public virtual Int64 RoomId { get; protected set; }
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
[Property]
[DataMember]
public virtual Int64 HotelId { get; set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
[Property]
[DataMember]
public virtual string Description { get; set; }
public static Room[] FindByHotelId(Int64 HotelId)
{
return (Room[])FindAllByProperty(typeof(Room), "HotelId", HotelId);
}
}
The RoomType class is
[ActiveRecord(Lazy = true)]
[DataContract]
public class RoomType : ActiveRecordBase<RoomType>
{
[PrimaryKey]
[DataMember]
public virtual int RoomTypeId { get; protected set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
}
I use the following method to serialise the object
internal static XElement ObjectToXElement<T>(T source)
{
XDocument oXDocument = new XDocument();
try
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(source.GetType());
serializer.WriteObject(writer, source);
}
}
catch(Exception e)
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(oError.GetType());
serializer.WriteObject(writer, oError);
}
}
return oXDocument.Root;
}
The actual object I'm serialising is;
[KnownType(typeof(List<Room>))]
[KnownType(typeof(RoomType))]
[DataContract]
public class RoomTypeResponse
{
[DataMember]
public int Code { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public List<Room> Rooms { get; set; }
public RoomTypeResponse()
{
this.Rooms = new List<Room>();
}
}
But for some reason when I call the method to serialise the object I get the following exception;
Type 'Castle.Proxies.RoomTypeProxy' with data contract name
'RoomTypeProxy:http://schemas.datacontract.org/2004/07/Castle.Proxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
If I comment out the property in Room class, it works fine
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
I'm not sure why I am getting the exception, because I've added the knowtype attribute for RoomType ? What am I missing, that is causing this problem.
The problem is that one type (Castle.Proxies.RoomTypeProxy) is generated at runtime, so .NET knows nothing about it. This is not an NHibernate-specific problem. If you disable lazy loading and proxy generation, the problem will go away, but I understand it might be difficult.
Other option would be to use another serializer, like BinaryFormatter, but I don't know if that will work for you.

Deserializing JSON from ASP.net web service into C# object

After spending a day reading through posts here I still can't get this to work so hopefully this makes sense to someone here.
The web service returns this simple JSON
{"d":{"__type":"TestWebServices.Person","Name":"Bob","FavoriteColor":"Green","ID":0}}
Then I am using C# code to deserialize
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(Person));
Person someone = (Person)jsonSerializer.ReadObject(responseStream);
When I use this model someone is created but all the properties are null
[DataContract]
public class Person {
[DataMember]
public string Name { get; set; }
[DataMember]
public string FavoriteColor { get; set; }
[DataMember]
public int ID { get; set; }
}
I tried being more literal and used this model
[DataContract]
public class Person {
[DataMember]
public PersonItem d { get; set; }
}
[DataContract]
public class PersonItem {
[DataMember]
public string __Type { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string FavoriteColor { get; set; }
[DataMember]
public int ID { get; set; }
}
And got this error, which I don't even know where to start with
Element ':d' contains data from a type that maps to the name ':GEMiniWebServices.Person'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'TestWebServices.Person' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.
Any thoughts?
Thanks
__Type should never be part of your object. It's a hint to the serializer. Also, the type hint that you have in your JSON object is bad. Stand-Alone JSON Serialization says:
To preserve type identity, when serializing complex types to JSON a
"type hint" can be added, and the deserializer recognizes the hint and
acts appropriately. The "type hint" is a JSON key/value pair with the
key name of "__type" (two underscores followed by the word "type").
The value is a JSON string of the form
"DataContractName:DataContractNamespace" (anything up to the first
colon is the name).
The type hint is very similar to the xsi:type attribute defined by the
XML Schema Instance standard and used when serializing/deserializing
XML.
Data members called "__type" are forbidden due to potential conflict
with the type hint.
It works with the following if you rewrite the __type declaration as Person:#TestWebServices or eliminate it:
namespace TestWebServices
{
[KnownType(typeof(Person))]
[DataContract]
public class PersonWrapper
{
[DataMember]
public Person d { get; set; }
}
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string FavoriteColor { get; set; }
[DataMember]
public int ID { get; set; }
}
}
Try adding (and I'm kind of taking a bit of a stab here so the exact namespace my be incorrect)
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/TestWebServices.Person")]
to your DataContractAttribute on Person.
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/TestWebServices.Person")]
public class Person {
[DataMember]
public PersonItem d { get; set; }
}
[DataContract]
public class PersonItem {
[DataMember]
public string __Type { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string FavoriteColor { get; set; }
[DataMember]
public int ID { get; set; }
}

Categories