How to deep clone the Class that contains DynamicObject in C#? - c#

public class DynamicDictionary : System.Dynamic.DynamicObject
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
{
return this.dictionary?.Keys;
}
}
public class SerializeExtensions
{
public string Name { get; set; }
public DynamicDictionary DynamicObj { get; set; }
}
Please consider the above like my class.
I have a List of SerializeExtensions collection. Now I have to deep clone this list of collections.
This has been properly cloned when I am using the below code.
JsonConvert.DeserializeObject<List<SerializeExtensions>>(JsonConvert.SerializeObject(collection));
This serialization belongs to Newtonsoft.Json. But I need to System.Text.Json for serialization.
So when I am using System.Text.Json serialization, the DynamicObject field has reset to empty. the below code has used.
JsonSerializer.Deserialize<List<SerializeExtensions>>(JsonSerializer.Serialize(collection));
After the serialization, I am getting the output like below
Name: "John"
DynamicObj: {}
Actually, the DynamicObj count is 4 in my case before serialization.
is there any possible to deep clone this kind of class?

Related

Deserialising JSON using JsonSerializer.DeserializeAsync is not using my JsonConverter

A server is returning a JSON string value which is a URL query string:
{
"parameters": "key1=value1&key2=value2"
}
I have a property set up to receive this, and convert it into a Dictionary as part of the deserialisation process:
Property with JsonConverter attribute:
[JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public Dictionary<string, string> Parameters { get; set; }
Converter:
public class QueryStringToDictionaryJsonConverter : JsonConverter<Dictionary<string, string>> {
public override Dictionary<string, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var queryString = reader.GetString();
if (string.IsNullOrEmpty(queryString)) return null;
return QueryHelpers.ParseQuery(queryString).ToDictionary(e => e.Key, e => string.Join(",", e.Value.ToArray()));
}
...
}
This should work.
But it's not even getting to my converter.
From what I can tell, JsonSerializer.DeserializeAsync<T>(myJson) is seeing that the type of property is a Dictionary, and so it tries to parse the value as such on its own, and fails (the resulting exception is an 'invalid cast' as it tries to GetEnumerable() etc). A breakpoint in my converter never even gets hit.
I can get it to work by making the property an object and then casting to a Dictionary later where it's used, but that's an ugly solution.
Is there a way to force JsonSerializer.DeserializeAsync<T>(myJson) to just use my converter, without it trying to be smart on its own?
(I'm using Microsoft's System.Text.Json in .NET Core 3)
OK, so this could be a bug in System.Text.Json.
Here's the workaround I'm currently using for anyone else needing a solution.
First, I set up two properties for deserialisation, using [JsonPropertyName] and [JsonIgnore]:
[JsonPropertyName("parameters"), JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public object ParametersObject { get; set; }
[JsonIgnore]
public Dictionary<string, string> Parameters => ParametersObject as Dictionary<string, string>;
And then in the JsonConverter, I allow object as the type:
public override bool CanConvert(Type typeToConvert) {
if (typeToConvert == typeof(object)) return true;
return base.CanConvert(typeToConvert);
}
Consumers of my deserialised class just use the Parameters property, which will continue to work just fine if and when this bug is fixed and I change the class back to how I'd like it.
I would create a wrapper and create a converter for the wrapper.
[JsonConverter( typeof( QueryStringDictionaryConverter ) )]
class QueryStringDictionary : Dictionary<string,string> { }
class QueryStringDictionaryConverter : JsonConverter<QueryStringDictionary>
{
...
}
class MyClass
{
public QueryStringDictionary Parameters { get; set; }
}
Alternatively you could use JsonSerializerOptions
class MyOtherClass
{
public Dictionary<string,string> Parameters { get; set; }
}
MyOtherClass Deserialize( string json )
{
var options = new JsonSerializerOptions
{
Converters = { new QueryStringToDictionaryJsonConverter() }
};
return JsonSerializer.Deserialize<MyOtherClass>( json, options );
}
A potential problem with this approach is that the converter would be used on all Dictionary<string,string> properties, which may not be intended. It would work fine for the simple example in the original question.

Deserialize JSON object into C# class that wraps typed Dictionary field

I have a JSON object that I get from my REST API server that looks like this:
"settings": {
"assets.last_updated_at": "2016-08-24T23:40:26.442Z",
"data.version": 34
}
Normally I'd deserialize the object to a Dictionary of string to object, but I'd like to provide helper methods to get strongly typed versions of the values. I'm using JSON.NET to deserialize the JSON object. Here's what I've got so far:
[JsonConverter(typeof(SettingsJsonConverter))]
public class Settings
{
[JsonIgnore]
public Dictionary<string, string> Entries { get; private set; }
public Settings()
{
this.Entries = new Dictionary<string, string>();
}
}
In order to wrap the dictionary within a class, it seems like I needed to create a custom JsonConverter, which currently looks like this:
public class SettingsJsonConverter : JsonConverter
{
#region implemented abstract members of JsonConverter
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject settingsObj = JObject.Load(reader);
Settings settings = new Settings();
foreach(KeyValuePair<string, JToken> entry in settingsObj)
{
settings.Entries.Add(entry.Key, entry.Value.ToString());
}
return settings;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Settings);
}
#endregion
}
Now obviously I could create methods in my Settings class like, GetDate that takes the value for a given key and creates a new DateTime object with the ISO string value. However, JSON.NET already provides facilities to do what I want to do, so I'd rather use its built-in deserialization for any values I iterate over, instead of doing it myself.
It seems as though the place to do this would be inside the foreach loop in SettingsJsonConverter#ReadJson, but I'm not quite sure how to get the Type (not a JTokenType, a standard C# Type) of a given JToken so I can pass it into JToken#ToObject(Type). Or, how can I iterate over a JsonReader to populate my internal Entries dictionary with already deserialized values, such that I just need to do simple casts in my helper methods in Settings? Here's how I'd like my Settings class to look:
[JsonConverter(typeof(SettingsJsonConverter))]
public class Settings
{
[JsonIgnore]
public Dictionary<string, object> Entries { get; private set; }
public Settings()
{
this.Entries = new Dictionary<string, object>();
}
public DateTime GetDate(string key)
{
return (DateTime)this.Entries[key];
}
}
Am I even doing this correctly? It almost feels like there's some simple way I'm overlooking.
You can directly deserialize into a Dictionary<string, JToken> and use type casts as you need them so you don't need a Settings class or a custom converter. It doesn't look as nice as your solution, but the code is simpler:
var settings = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(json);
var date = (DateTime) settings["key"];
If you want to keep your Settings class, I'd suggest that you use the above line to deserialize the dictionary and create a new constructor in Settings with a dictionary parameter, so you end up with two constructors, one that creates a new dictionary, and one that uses a given dictionary to create the new one. (Don't re-use the parameter dictionary itself, you never know what the caller will do to the dictionary afterwards.)
public Settings(Dictionary<string, JToken> dict)
{
Entries = new Dictionary<string, JToken>(dict);
}
By the way, you can remove the JsonIgnore attribute because you have a custom converter class.
First of all we have to make a model for your corresponding json.
{
"settings": {
"assets.last_updated_at": "2016-08-24T23:40:26.442Z",
"data.version": 34
}
}
CSharp Model:
public class Settings
{
[JsonProperty("assets.last_updated_at")]
public string assets_last_updated_at { get; set; }
[JsonProperty("data.version")]
public int data_version { get; set; }
// Add other property here...
}
public class RootObject
{
public Settings settings { get; set; }
}
Deserialize Json string to Model :
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>("Put your Json String");
Hope this help.

Deserializing inherited classes

I want to send parameter-settings from a backendapplication to the frontend. I also need to be able to have different type of parameters (Portnumbers, folders, static strings and such).
So I've designed a baseclass, Parameter as such:
public abstract class Parameter
{
public abstract bool isValid();
}
Let's say that we have two types of folder parameters:
public abstract class Folder : Parameter
{
public string folderName { get; set; }
protected Folder(string folderName)
{
this.folderName = folderName;
}
}
public class ReadWriteFolder : Folder
{
public ReadWriteFolder(string folderName) : base(folderName)
{
}
public override bool isValid()
{
return isReadable() && isWritable();
}
}
public class ReadFolder : Folder
{
public ReadFolder(string folderName) : base(folderName)
{
}
public override bool isValid()
{
return isReadable();
}
}
This is used from a WebAPI, so this is my controller:
public Dictionary<String, Parameter> Get()
{
Dictionary<String, Parameter> dictionary = new Dictionary<String, Parameter>();
dictionary.Add("TemporaryFiles", new ReadWriteFolder("C:\\temp\\"));
dictionary.Add("AnotherTemporaryFiles", new ReadWriteFolder("D:\\temp\\"));
return dictionary;
}
This yields the following JSON-serialisation:
{"TemporaryFiles":{"folderName":"C:\\temp\\"},"AnotherTemporaryFiles":{"folderName":"D:\\temp\\"}} which seems reasonable.
My question is this: How can I deserialize this back into the original types? Or change the serialization into something that is more easy to deserialize?
What are you using for serialization? If it's JSON.Net (which many here would suggest!), there's a number of realted questions:
how to deserialize JSON into IEnumerable with Newtonsoft JSON.NET
But the crux is the type name handling, which will decorate the elements with the type information to be able to deserialize them:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
string strJson = JsonConvert.SerializeObject(dictionary, settings);
And then you should be able to deserialize directly.
var returnDictionary = JsonConvert.DeserializeObject<Dictionary<String, Parameter>>(strJson, settings)

JSON.NET serialization if override ToString

I have a set of complex business objects that I'd like to serialize to Json for use in a web service. I'm currently using the DataContractJsonSerializer to product the Json, but it balks on deserialization because the default XmlReader can't handle Base64 strings.
After reading many positive reviews of Json.Net, I decided to give it a try. Surprisingly, the simplest case produces erroneous output if the business object overrides the ToString() method. Instead of generating JSON, it simply emits the string value.
For example, the following statement produces only a string, as the serializer appears to view the object as a simple string.
public class MyClass {
public string Title{get;set;}
public override ToString(){ return Title; }
public string ToJson(){
return JsonConvert.SerializeObject(this);
}
}
Instead of json formatted output, all I get is the title string. Do I have to mark the object in some special way to avoid this? Since the business object hierarchy includes many objects that override ToString(), I would rather avoid having to introduce special attributes, etc.
Is it possible that your actual class has a TypeConverterAttribute attached to it? I just ran into the exact same problem and found out that the TypeConverterAttribute was causing this. In thast case, this might help (at least it did for me).
This is very bad because you can inadvertently break your program (by simply adding a TypeConverter maybe for displaying the object in a PropertyGrid) without getting a compiler warning...
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var with = new WithTypeConverter() { Bla = 12, Blub = "With" };
var without = new WithoutTypeConverter() { Bla = 12, Blub = "Without" };
Console.WriteLine(with);
Console.WriteLine(JsonConvert.SerializeObject(with));
Console.WriteLine(without);
Console.WriteLine(JsonConvert.SerializeObject(without));
Console.ReadKey();
}
}
public class baseClass
{
public int Bla { get; set; }
public string Blub { get; set; }
public override string ToString()
{
return String.Format("{0}: {1}", this.GetType().Name, Blub);
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class WithTypeConverter : baseClass
{
}
public class WithoutTypeConverter : baseClass
{
}
}
You may be testing this wrong. I just ran the following code in LINQPad:
void Main()
{
new MyClass{Title = "hi"}.ToJson().Dump();
}
// Define other methods and classes here
public class MyClass {
public string Title{get;set;}
public override string ToString(){ return Title; }
public string ToJson(){
return JsonConvert.SerializeObject(this);
}
}
Output:
{"Title":"hi"}
I suspect you are using MyClass as the key in a dictionary or hashtable?
Linqpad example:
void Main()
{
object thing = new Dictionary<MyClass, MyClass>() {
{
new MyClass { Title = "hi" }, new MyClass { Title = "bye" }
}
};
JsonConvert.SerializeObject(thing).Dump();
}
public class MyClass
{
public string Title { get; set; }
public override string ToString() { return "BROKEN"; }
}
Output:
{"BROKEN":{"Title":"bye"}}
This is the expected behaviour as there is no way to express a complex object as a key in json.
To work around this either change your model or implement a TypeConverter.
If your object is simple enough you could have the ConvertTo and ConvertFrom simply read and write parameters in a given order.
[EDIT]
This turned out to be more simple than I'd expected. Here is my JsonConverter solution.
public class ObjectKeyDictionaryTypeConverter<T1, T2> : JsonConverter<Dictionary<T1, T2>>
{
public override void WriteJson(JsonWriter writer, Dictionary<T1, T2> value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToArray());
}
public override Dictionary<T1, T2> ReadJson(JsonReader reader, Type objectType, Dictionary<T1, T2> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var items = serializer.Deserialize(reader) as KeyValuePair<T1,T2>[];
return items?.ToDictionary(a => a.Key, a => a.Value);
}
}
Usage:
[JsonConverter(typeof(ObjectKeyDictionaryTypeConverter<ICriteria, Results>))]
public Dictionary<ICriteria, Results> SearchesAndResults { get; set; }
Use JsonSerializer from System.Text.Json to serialize the class. Like so:
using System.Text.Json;
...
public class Foo{
Public String Title {get;set;}
public override ToString(){
return JsonSerializer.Serialize<Foo>(this);
}
}
Docs:
https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer?view=netcore-3.1
Context:
https://youtu.be/JfnTG955cuk?t=406

Using JavaScriptConverter for DataContract serialization in C#

I am having problem to find some information about how to get a custom converter invoked for a given property when serializing a C# object to JSON (and vice-versa).
JSON looks like that:
{"ws_status": "success", "result": 32}
or
{"ws_status": "failure", "error_code": 32123}
I have my C# object:
[DataContract]
class WebServiceResult
{
[DataMember(Name = "ws_status", IsRequired = true)]
public Boolean Success { get; private set; }
[DataMember(Name = "error_code")]
public Int32 ErrorCode { get; private set; }
[DataMember(Name = "result")]
public Int32 Result { get; private set; }
}
What I am missing is how to get the JSON values "success" and "failure" converted to a boolean telling me if the WS was successful.
I have implemented a JavaScriptConverter but I don't know how to bind it to my C# object's property.
class JsonStatusConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(Boolean) })); }
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Here, do we get a WebServiceResult or directly the WebServiceResult.Success
// object?
Boolean success = obj==null ? false : (Boolean) obj;
Dictionary<string, object> result = new Dictionary<string, object>();
result["ws_status"] = success ? "success" : "error";
return result;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
if (type == typeof(String))
{
String status = dictionary["ws_status"] as String;
if (status.Equals("success")) return true;
else return false;
}
return null;
}
}
Maybe I misunderstood the concept of JavaScriptConverter and it can only be implemented for the whole WebServiceResult object (which would be a pity because most of the properties are standard).
PS: I know I could simply get the status serialized directly to a string and have a helper method converting that to a boolean in the C# object, but I'd like to learn about converters as I will need them in some other objects.
JsonSerializer/JavaScriptSerializer and DataContractJsonSerializer are distinct entities. DataContractJsonSerializer does not support even the concept of JavaScriptConverters, and JavaScriptSerializer in turn does not honor the DataContract programming model. In addition, with DataContractJsonSerializer, there is no way to do what you want to do here, at all, because boolean is a fundamental primitive in its serialization model, and in its world, extensibility points for primitives are severely curtailed, if not non-existent.
I think your best bet here (sadly) is to ditch this entire model altogether and use Json .NET

Categories