I have the following problem-
I'm trying to serialize a class which contains a class that has an additional dictionary.
The structure is simplified to the following:
public class GroupVM
{
public GroupVM()
{
this.Clusters = new Dictionary<int, ClusterVM>();
}
public Dictionary<int,ClusterVM> Clusters { get; set; }
}
public class ClusterVM
{
public ClusterVM()
{
this.Attributes = new Dictionary<Guid, AttributeVM>();
}
Dictionary<Guid,AttributeVM> Attributes { get; set; }
public void AddAttribute(Guid guid, string name)
{
AttributeVM attrVM = new AttributeVM();
attrVM.Name = name;
attrVM.Guid = guid;
this.Attributes.Add(guid,attrVM);
}
}
public class AttributeVM
{
public Guid Guid { get; set; }
public string Name { get; set; }
}
I'm trying to use it in API, and return a serialized version of GroupVM. For some reason, I'm getting nothing in the Attributes Dictionary (inside ClusterVM class).
If I change it to List, it works fine.
Code Sample
According to the sample code the Attributes property was not public
Dictionary<Guid,AttributeVM> Attributes { get; set; }
It could not get serialized because the serializer was unaware of its presence. Make the property public and it should get serialized.
public class ClusterVM {
public ClusterVM() {
this.Attributes = new Dictionary<Guid, AttributeVM>();
}
public IDictionary<Guid,AttributeVM> Attributes { get; set; }
public void AddAttribute(Guid guid, string name) {
AttributeVM attrVM = new AttributeVM();
attrVM.Name = name;
attrVM.Guid = guid;
this.Attributes.Add(guid,attrVM);
}
}
Related
I have this entity from the database
public class SettingsDb
{
public int Id { get; set; }
public string Section { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
It means:
Section: the main section of settings (e.g., FTP)
Name: name of the property (e.g., Enabled, Server etc.)
Value: value of the property (e.g. true, "www.server.it" etc)
The model/to class it is
public class FtpSettings
{
public bool Enabled { get; set; }
public string Server { get; set; }
public int Port { get; set; }
public string Protocol { get; set; }
public string SSLStartMode { get; set; }
public string User { get; set; }
public string Password { get; set; }
}
I have to flat the list IList to a single model/dto object FtpSettings (supposing it is already filtered from the section from SQL query)
How can I achieve this?
Thank you.
//your model
public class FtpSettings
{
// tle IList that you want
public IList<SettingsDb> flatIlist {get;set;}
}
Or you can just add your Ilist directly in your View like this:
#model IList<SettingsDb>
after troubling I did the following.
A converter
public static class ConfigConverter<T> where T : class, new()
{
public static T GetSettings(IList<SettingsDb> settings)
{
var cfg = new MapperConfiguration(cfg =>
cfg.CreateMap<IList<SettingsDb>, T>()
.ConvertUsing<SettingsTypeConverter<T>>());
var mapper = cfg.CreateMapper();
var ftpSettings = mapper.Map<T>(settings);
return ftpSettings;
}
}
public class SettingsTypeConverter<T> : ITypeConverter<IList<SettingsDb>, T> where T : class, new()
{
public T Convert(IList<SettingsDb> source, T destination, ResolutionContext context)
{
var dto = new T();
foreach (var setting in source)
{
foreach (var dtoProperty in dto.GetType().GetProperties())
{
if (!setting.Name.Equals(dtoProperty.Name))
{
continue;
}
var tProp = dtoProperty.PropertyType;
if (!setting.Value.IsConvertibleTo(tProp))
{
//error
continue;
}
dtoProperty.SetValue(dto, setting.Value.To(tProp));
}
}
return dto;
}
It can be used as
var ftpSettings = ConfigConverter<FtpSettings>.GetSettings(settingsDb);
It must be installed AutoMapper and UniversalTypeConverter by Nuget
I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.
So I'm making a small JSON Parser.
This is the JSON I want to parse:
{"158023":{"prices":{"xbox":{"LCPrice":"225,000","LCPrice2":"232,000","LCPrice3":"235,000","LCPrice4":"235,000","LCPrice5":"239,000","updated":"15 secs ago","MinPrice":"27,000","MaxPrice":"500,000","PRP":"41"},"ps":{"LCPrice":"228,000","LCPrice2":"231,000","LCPrice3":"232,000","LCPrice4":"233,000","LCPrice5":"235,000","updated":"9 mins ago","MinPrice":"30,000","MaxPrice":"550,000","PRP":"38"},"pc":{"LCPrice":"305,000","LCPrice2":"305,000","LCPrice3":"315,000","LCPrice4":"333,000","LCPrice5":"347,000","updated":"1 hour ago","MinPrice":"37,000","MaxPrice":"700,000","PRP":"40"}}}}
And I have the following class, to represent the Json Object.
public partial class Prices
{
[JsonProperty("158023")]
public Token TokenNumber { get; set; }
}
public partial class Token
{
[JsonProperty("prices")]
public PricesClass Prices { get; set; }
}
public partial class PricesClass
{
[JsonProperty("xbox")]
public Pc Xbox { get; set; }
[JsonProperty("ps")]
public Pc Ps { get; set; }
[JsonProperty("pc")]
public Pc Pc { get; set; }
}
public partial class Pc
{
[JsonProperty("LCPrice")]
public string LcPrice { get; set; }
[JsonProperty("LCPrice2")]
public string LcPrice2 { get; set; }
[JsonProperty("LCPrice3")]
public string LcPrice3 { get; set; }
[JsonProperty("LCPrice4")]
public string LcPrice4 { get; set; }
[JsonProperty("LCPrice5")]
public string LcPrice5 { get; set; }
[JsonProperty("updated")]
public string Updated { get; set; }
[JsonProperty("MinPrice")]
public string MinPrice { get; set; }
[JsonProperty("MaxPrice")]
public string MaxPrice { get; set; }
[JsonProperty("PRP")]
public string Prp { get; set; }
}
public partial class Prices
{
public static Prices FromJson(string json) => JsonConvert.DeserializeObject<Prices>(json, PriceConverter.Settings);
}
internal static class PriceConverter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
I'm easily able to parse the JSON, by doing:
Prices prices = Prices.FromJson(myJson);
The problem is when I want to use a different number than 158023.
For example, 158025.
The JsonProperty on the Prices class is already set to "158023", and I have no clue how to rename it.
TLDR:
I have a JSON Object, which I want to rename the JsonProperty text, before deserializing.
Since you don't know the key, use a Dictionary<string, Token> instead of the property TokenNumber in class Prices.
public partial class Prices
{
// Remove this property
// [JsonProperty("158023")]
// public Token TokenNumber { get; set; }
}
public partial class Prices
{
public static Dictionary<string, Token> FromJson(string json) => JsonConvert.DeserializeObject<Dictionary<string, Token>>(json, PriceConverter.Settings);
}
Now the result will be a dictionary where the key is the token value as an string and the value is a Token object.
You could make use of the JsonExtensionData and OnDeserialized attributes:
public class Wrapper
{
public string Id { get; set; }
public Item Item { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
// Get the first key. If you have more than one, you may need
// to customize this for your use case
var id = _additionalData.Keys.FirstOrDefault();
if (id != null)
{
// Assign to our Id property
this.Id = id;
// Create a reader for the subobject
var itemReader = _additionalData[id].CreateReader();
var serializer = new JsonSerializer();
// Deserialize the subobject into our Item property
this.Item = serializer.Deserialize<Item>(itemReader);
itemReader.Close();
}
}
}
public class Item
{
public string Name { get; set; }
}
You can try it here. Alternatively you could write a JsonConverter to achieve the same thing, or do what Amir suggested.
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
// Deserialize the response to get an array of CUSTOM Cases
var reportsList = jsSerializer.Deserialize<SfdcObjects.SfdcCollection<SfdcObjects.Assets>>(HttpUtility.UrlDecode(response));
throws an exception:
Error: System.InvalidOperationException: Type 'SalesforceDataQueryComponent.Utils.SfdcObjects+SfdcCollection`1[
[SalesforceDataQueryComponent.Utils.SfdcObjects+Assets, SalesforceDataQueryComponent, Version=1.2.0.0, Culture=neutral]]'
is not supported for deserialization of an array.
I can not figure it out the issue:
Objects:
namespace SalesforceDataQueryComponent.Utils
{
class SfdcObjects
{
// Used for Authentication
public class TokenResponse
{
public string id { get; set; }
public string issued_at { get; set; }
public string refresh_token { get; set; }
public string instance_url { get; set; }
public string signature { get; set; }
public string access_token { get; set; }
}
// All classes shown next are used to parse the HttpGet Response
public class SfdcCollection<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
public class SfdcAttributes
{
public string Type { get; set; }
public string Url { get; set; }
}
public class Accounts : Account
{
public SfdcAttributes Attributes { get; set; }
}
public class Assets : Asset
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomAssets : Assets
{
public string StringInstallDate { get; set; }
}
public class Users : User
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomCase : Case
{
public string StringCreatedDate { get; set; }
}
public class CustomCases : CustomCase
{
public SfdcAttributes Attributes { get; set; }
}
}
}
You do not include your response JSON in your question, however from the error message, your problem must be that the root JSON container in your response is an array. A JSON array, according to the JSON standard, looks like this:
[value1, value2, ..., valueN]
JSON serializers map types that implement ICollection or IEnumerable from and to JSON arrays.
Your root object SfdcCollection<T>, however, is NOT a collection or enumerable, despite its name. Instead it's a non-enumerable generic POCO:
public class SfdcCollection<T> // No IEnumerable<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
Thus a serializer will map this to a JSON object (which is a set of key/value pairs and looks like {"name1" : value1, "name2" : value2, ..., "nameN" : valueN }) instead of an array.
You need to update your data model to the JSON you are actually receiving. Try uploading your JSON to http://json2csharp.com/, it will automatically generate classes for you.
If you must use the classes in your question, you could ask another question about how to map the JSON you are actually receiving onto your required classes, using your desired serializer (e.g. Json.NET, DataContractJsonSerializer, JavaScriptSerializer, or etc.)
I'm trying to serialize a class derived from List<> using DataContract. The problem is that properties of my class won't get serialized.
My classes:
[CollectionDataContract] /*[Serializable]*/ /*[DataContract]*/
public class DownloadRuleCollection : List<DownloadRule> {
[DataMember(EmitDefaultValue = false)]
public string SomeProperty { get; set; }
//this is, in fact, more complex, but this is enough for the example
}
[DataContract]
public class DownloadRule {
[DataMember(EmitDefaultValue = false)]
public string Name { get; set; }
/*
more properties
...
*/
}
Test:
static void Main(string[] args) {
//fill test collection with some data...
var col = new DownloadRuleCollection { SomeProperty = "someText" };
var rule = new DownloadRule { Name = "test01" };
col.Add(rule);
rule = new DownloadRule { Name = "test02" };
col.Add(rule);
rule = new DownloadRule { Name = "test03" };
col.Add(rule);
//serialize
Console.WriteLine("serializing");
Serialize(col, "serializationTest.xml");
Console.WriteLine("serialized");
Console.ReadLine();
}
result:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfDownloadRule xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
<DownloadRule>
<Name>test01</Name>
</DownloadRule>
<DownloadRule>
<Name>test02</Name>
</DownloadRule>
<DownloadRule>
<Name>test03</Name>
</DownloadRule>
</ArrayOfDownloadRule>
As you can see, the List's items are serialized (and deserialized) properly, but the List itself does not get serialized. I have tried to use different attributes:
[Serializable], no change;
[DataContract], throws exception during serialization (collections cant use this attribute)
btw I am serializing also private fields, so I cannot use XmlSerializer (or other classes that can't serialize private fields).
Use an IList instead. That should serialize properly.
[CollectionDataContract] /*[Serializable]*/ /*[DataContract]*/
public class DownloadRuleCollection : IList<DownloadRule> {
Here is an example i use and works perfectly:
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/InboundIntegration.HL7Messaging")]
public class Message {
public Message() {
InsuranceList = new List<Insurance>();
MessageId = GuidComb.NewGuid();
}
[IgnoreDataMember]
public Guid MessageId { get; private set; }
#region "Data"
[DataMember]
public string MessageTypeIndicator { get; set; }
[DataMember]
public MessageConfiguration MessageConfiguration { get; set; }
[DataMember]
public Patient Patient { get; set; }
[DataMember]
public Encounter Encounter { get; set; }
[DataMember]
public IList<Insurance> InsuranceList { get; set; }
#endregion
Then insurance class looks like this:
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/InboundIntegration.HL7Messaging")]
public class Insurance {
[DataMember]
public string ExternalPayerId { get; set; }
[DataMember]
public string PayerName { get; set; }
[DataMember]
public string GroupNumber { get; set; }
[DataMember]
public string MemberIdOfPatient { get; set; }
[DataMember]
public string PatientRelationshipToInsuredIndicator { get; set; }
[DataMember]
public string CoordinationOfBenefitsPrecedenceIndicator { get; set; }
Ok, so Climber104's solution would work, but I would need to re-implement all of the List's methods, which makes me feel that I am reinventing the wheel.
JaredPar from Jarek Waliszko's thread suggests to use a wrapper class.
The easiest is to use it just for the sake of serialization process, so I used an protected innner wrapper class. This allows me to achieve my goals with just a few lines of code needed.
public class DownloadRuleCollection : List<DownloadRule> {
public string SomeProperty { get; set; }
public void Serialize(string fileName) {
Serializer.Serialize(
new DownloadRuleCollection_SerializationWrapper {
Collection = this,
SomeProperty = SomeProperty
}, fileName);
}
public static DownloadRuleCollection Deserialize(string fileName) {
var wrapper = Serializer.Deserialize<DownloadRuleCollection_SerializationWrapper>(fileName);
var result = wrapper.Collection;
result.SomeProperty = wrapper.SomeProperty;
return result;
}
[DataContract(Name = "DownloadRuleCollection")]
private class DownloadRuleCollection_SerializationWrapper {
[DataMember(EmitDefaultValue = false, Name = "SomeProperty", Order = 0)]
public string SomeProperty { get; set; }
[DataMember(EmitDefaultValue = false, Name = "DownloadRules", Order = 1)]
public DownloadRuleCollection Collection;
}
}
[DataContract]
public class DownloadRule {
[DataMember(EmitDefaultValue = false)]
public string Name { get; set; }
}
public static class Serializer {
public static void Serialize<T>(T obj, string fileName) {
using(XmlWriter writer = XmlWriter.Create(fileName, new XmlWriterSettings { Indent = true }))
new DataContractSerializer(typeof(T)).WriteObject(writer, obj);
}
public static T Deserialize<T>(Stream stream) {
return (T)new DataContractSerializer(typeof(T)).ReadObject(stream);
}
public static T Deserialize<T>(string fileName) {
using(FileStream fs = File.OpenRead(fileName)) {
return Deserialize<T>(fs);
}
}
}