How to create custom serializer for values in dictionary? - c#

I have the following class:
public class Farm
{
public string County {get;set;}
public Dictionary<string, object> FarmItems {get;set;}
}
I need to implement a custom serializer/deserializer that, if it's a integer in the value, store it in the DB as a string and then deserialize it back to a integer
I've looked at the docs and they gave details on how to do it for single values but not for dictionaries https://mongodb.github.io/mongo-csharp-driver/2.12/reference/bson/serialization/
Also, the docs don't mention how to implement it, do I just add an attribute after creating my class like so:
[BsonSerializer(typeof(MyCustomSerializer))]
public Dictionary<string, object> FarmItems {get;set;}

here you go:
public class MyDictionarySerialzer : SerializerBase<Dictionary<string, object>>
{
public override void Serialize(BsonSerializationContext ctx, BsonSerializationArgs args, Dictionary<string, object> dictionary)
{
if (dictionary == null)
{
ctx.Writer.WriteStartArray();
ctx.Writer.WriteEndArray();
return;
}
ctx.Writer.WriteStartArray();
foreach (var kvPair in dictionary)
{
ctx.Writer.WriteStartDocument();
ctx.Writer.WriteName("k");
ctx.Writer.WriteString(kvPair.Key);
ctx.Writer.WriteName("v");
if (kvPair.Value is int)
ctx.Writer.WriteString(kvPair.Value.ToString());
else
BsonSerializer.Serialize(ctx.Writer, kvPair.Value);
ctx.Writer.WriteEndDocument();
}
ctx.Writer.WriteEndArray();
}
public override Dictionary<string, object> Deserialize(BsonDeserializationContext ctx, BsonDeserializationArgs args)
{
Dictionary<string, object> dict = new();
switch (ctx.Reader.CurrentBsonType)
{
case BsonType.Array:
foreach (var val in BsonSerializer.Deserialize<BsonArray>(ctx.Reader))
{
string key = val["k"].AsString;
object value = BsonSerializer.Deserialize<object>(val["v"].ToJson());
if (int.TryParse(value?.ToString(), out var parsedInt))
value = parsedInt;
dict.Add(key, value);
}
return dict;
default:
throw new BsonSerializationException("Unable to deserialize dictionary!");
}
}
}
if you are storing any complex entities in the dictionary values, you have to register (on app startup) that complex type with mongo driver like so (or deserialization will fail):
BsonClassMap.RegisterClassMap<ComplexItem>();
simply decorate the property like so:
public class Farm
{
[BsonSerializer(typeof(MyDictionarySerialzer))]
public Dictionary<string, object> FarmItems { get; set; }
}
example entity:
var example = new Farm
{
FarmItems = new Dictionary<string, object>()
{
{"empty",null },
{"string","test string" },
{"int",100 },
{"complex",new ComplexItem{ Age = 10, Name = "test" } }
}
};
it will be serialized to the db like so:
{
"FarmItems" : [
{
"k" : "empty",
"v" : null
},
{
"k" : "string",
"v" : "test string"
},
{
"k" : "int",
"v" : "100"
},
{
"k" : "complex",
"v" : {
"_t" : "ComplexItem",
"Name" : "test",
"Age" : 10
}
}
]
}

Related

return custom json response using strongly typed model

My response model class
public class MyModel
{
public Tuple<string, string> Data{ get; set; }
}
var data = new MyModel
{
Data = Tuple.Create("error", "12345");
};
which results in the following json response
{
"data": {
"item1": "error",
"item2": "12345"
}
}
What I want is to create a response as follows
"data" : [{ "error" : "12345" }]
These item1 and item2 are of course dynamic data and it can be anything of type string. I'm saying this because I cannot simply decorate the properties with JsonProperty attribute.
You need either to implement custom serializer for your tuple or change your model to represent the desired structure. If you don't want to create some specific types for your inner data you can leverage Dictionary serialization convention:
public class MyModel
{
public Dictionary<string, string>[] Data{ get; set; }
}
And creation:
var data = new MyModel
{
Data = new[]
{
new Dictionary<string, string> { { "error", "message" } }
}
};
Or make your field into array of objects and create anonymous ones:
public class MyModel
{
public object[] Data{ get; set; }
}
var data = new MyModel
{
Data = new[]
{
new { error = "message" }
}
}

Prepare custom json format return in MVC c#

I have two classes, namely Roles and Module in Module class I have two properties Name,Permission.
for now, just testing purpose I'm creating one demo code.
Class file
public class Roles
{
public Module[] modules { get; set; }
public Roles()
{
modules = new Module[] { };
}
}
public class Module
{
public string Name { get; set; }
public string[] Permission { get; set; }
}
Controller code
public ActionResult Index()
{
var oRoles = new Roles();
oRoles.modules = new Module[] {
new Module(){
Name="Page-Profile",
Permission=new string[]{ "Edit","View","Delete"}
},
new Module(){
Name="User",
Permission=new string[]{ "Edit","View","Delete","Update"}
}
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(oRoles);
return View(json);
}
It's working fine I'm getting result also but I want result like this.
Expected result.
{
"modules": [
{
"Page-Profile": [
"Edit",
"View",
"Delete"
]
},
{
"User": [
"Edit",
"View",
"Update",
"Delete"
]
}
]
}
Currently I'm getting a result like this. is there any way to modify JSON remove all key and get only values.
{
"modules": [
{
"Name": "Page-Profile",
"Permission": [
"Edit",
"View",
"Delete"
]
},
{
"Name": "User",
"Permission": [
"Edit",
"View",
"Delete",
"Update"
]
}
]
}
Note: How to remove key properties i want only name value and in name value again i wan't all permission as mention in excpected result.
Its working for me ,try this :
Code
public class Roles
{
public Roles()
{
modules = new List<Dictionary<string, List<string>>>();
}
public List<Dictionary<string, List<string>>> modules { get; set; }
}
/*Index*/
public ActionResult Index()
{
var oRoles = new Roles();
var userRolePermissionsAndModulesList = userRolePermissionManager.GetAllUserRolePermissionsAndModules(userId);
foreach (var module in userRolePermissionsAndModulesList)
{
var objPermissionWithModules = new Dictionary<string, List<string>>();
var permission = new List<string> { };
if (module.CanCreate)
permission.Add("Create");
if (module.CanDelete)
permission.Add("Delete");
if (module.CanEdit)
permission.Add("Update");
if (module.CanView)
permission.Add("View");
objPermissionWithModules.Add(module.ModuleName, permission);
oRoles.modules.Add(objPermissionWithModules);
}
var json = Newtonsoft.Json.JsonConvert.SerializeObject(oRoles);
return View(json);
}
Please compare your output :
{
"modules":[
{
"Page-Profile":[
"Edit",
"View",
"Delete"
]
},
{
"user":[
"Edit",
"View",
"Delete",
"Update"
]
}
]
}
Remove Module class and try changing Roles as follows to get expected json format:
public class Roles
{
public Roles()
{
modules = new Dictionary<string, List<string>>();
}
public Dictionary<string, List<string>> modules { get; set; }
}
Controller Action code:
public ActionResult Index()
{
var oRoles = new Roles();
oRoles.modules.Add("Page-Profile", new List<string>{"Edit","View","Delete"});
oRoles.modules.Add("user", new List<string>{"Edit","View","Delete","Update"});
// Not sure why you are serializing to json.
// you can directly return View(oRoles) or PartialView(oRoles)
var json = Newtonsoft.Json.JsonConvert.SerializeObject(oRoles);
return View(json);
}
There are two way to achieve it
By writing the custom converter
By changing the Roles object
By writing the custom converter
You can implement the CustomJsonConverter as follows which convert the name to key and permission to value:
public class FlatternKeysConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Module o = (Module)value;
JObject newObject = new JObject(new JProperty(o.Name, o.Permission));
newObject.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
And attach the JsonConverter as
[JsonConverter(typeof(FlatternKeysConverter))]
public class Module
{
public string Name { get; set; }
public string[] Permission { get; set; }
}
The generated JSON will be as follows :
{"modules":[{"Page-Profile":["Edit","View","Delete"]},{"User":["Edit","View","Delete","Update"]}]}
By changing the Roles object
Change the roles object to dictionary
public class Role
{
public class Roles
{
public Dictionary<string, List<string>> Modules {get; set;}
}
}
You can check the https://dotnetfiddle.net/Hs3i04 dotnet fiddle which shows both approaches.

Mondodb C# save dictionary with array value

I have a mongo collection: Fieldset . Fieldset has List if embedded object Field.
Field has a Dictionary<string, object> Config, I want to be free about the value of this dictionary.
public class Fieldset
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Slug { get; set; }
[BsonElement("Fields")]
public List<Field> Fields { get; set; }
}
public class Field
{
[BsonElement("Config")]
public Dictionary<string, object> Config { get; set; }
}
this is an example of value i want to put in. some sort of key value collection:
When I save, this is the value I have in the mongo.
Hmm. It worked like supposed to work>
static void Main(string[] args)
{
var client = new MongoClient();
var database = client.GetDatabase("SO3");
var collection = database.GetCollection<Fieldset>("jobject");
var id = new BsonObjectId(ObjectId.GenerateNewId()).ToString();
var field =
new Field
{
Config = new Dictionary<string, object> {{"value", "item1key"}, {"lable", "item1value"}}
};
var field2 =
new Field
{
Config = new Dictionary<string, object> {{"value", "item2key"}, {"lable", "item2value"}}
};
var fieldset = new Fieldset
{
Id = id,
Slug = "aa",
Fields = new List<Field>
{
field,
field2
}
};
collection.InsertOne(fieldset);
}
{ "_id" : ObjectId("5c55b972ceb9443d385de936"), "Slug" : "aa", "Fields" : [ { "Config" : { "value" : "item1key", "lable" : "item1value" } }, { "Config" : { "value" : "item2key", "lable" : "item2value" } } ] }

How To Serialize a class that derives from a Dictionary

I am attempting to serialize/deserialize the following class to and from Json using Json.Net:
public class ChildDictionary:Dictionary<Employee, double>
{
public string Name { get; set; }
}
I have found the information here, here, and here that are related but none of them deal specifically with what the syntax should look like for this case where we derive from a Dictionary.
Employee successfully serializes with Json.Net on its own. It looks like this:
[JsonObject(MemberSerialization.OptIn)]
public class Employee
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public double Factor { get; set; }
[JsonProperty]
public List<ILoadBuilder> LoadBuilders = new List<ILoadBuilder>();
[JsonConstructor]
public LoadCause(string name, double factor, List<ILoadBuilder> loadBuilders)
{
this.Name = name;
this.DurationFactor = Factor;
this.LoadBuilders = loadBuilders;
}
}
I don't care what the Json looks like in the end as long as I can write and read it without losing data
Any suggestions of what the code to accomplish this should look like? Both a Custom JsonConverter or Attributes are fine solutions.
Because your dictionary has both a complex key and additional properties, you will need to use a custom JsonConverter to serialize and deserialize this class. Below is a converter that should do the job. It handles the serialization in two parts: first it uses reflection to deal with any read-write properties on the class, then it casts the object to a dictionary interface to handle the key-value pairs. The latter are written to the JSON as an array of objects with Key and Value properties so that the complex keys are managed without needing to jump through extra hoops.
public class ComplexDictionaryConverter<K,V> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<K,V>)));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject obj = new JObject();
foreach (PropertyInfo prop in GetReadWriteProperties(value.GetType()))
{
object val = prop.GetValue(value);
obj.Add(prop.Name, val != null ? JToken.FromObject(val, serializer) : new JValue(val));
}
JArray array = new JArray();
foreach (var kvp in (IDictionary<K, V>)value)
{
JObject item = new JObject();
item.Add("Key", JToken.FromObject(kvp.Key, serializer));
item.Add("Value", kvp.Value != null ? JToken.FromObject(kvp.Value, serializer) : new JValue(kvp.Value));
array.Add(item);
}
obj.Add("KVPs", array);
obj.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
IDictionary<K, V> dict = (IDictionary<K, V>)Activator.CreateInstance(objectType);
foreach (PropertyInfo prop in GetReadWriteProperties(objectType))
{
JToken token = obj[prop.Name];
object val = token != null ? token.ToObject(prop.PropertyType, serializer) : null;
prop.SetValue(dict, val);
}
JArray array = (JArray)obj["KVPs"];
foreach (JObject kvp in array.Children<JObject>())
{
K key = kvp["Key"].ToObject<K>(serializer);
V val = kvp["Value"].ToObject<V>(serializer);
dict.Add(key, val);
}
return dict;
}
private IEnumerable<PropertyInfo> GetReadWriteProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite && !p.GetIndexParameters().Any());
}
}
To use the converter, you can mark your class with a [JsonConverter] attribute like this (be sure the generic parameters match those of the dictionary your class inherits from):
[JsonConverter(typeof(ComplexDictionaryConverter<Employee, double>))]
public class ChildDictionary : Dictionary<Employee, double>
{
...
}
Here is a demo showing a full round-trip:
class Program
{
static void Main(string[] args)
{
ChildDictionary dict = new ChildDictionary();
dict.Name = "Roster";
dict.Add(new Employee { Id = 22, Name = "Joe", HireDate = new DateTime(2012, 4, 17) }, 1923.07);
dict.Add(new Employee { Id = 45, Name = "Fred", HireDate = new DateTime(2010, 8, 22) }, 1415.25);
string json = JsonConvert.SerializeObject(dict, Formatting.Indented);
Console.WriteLine(json);
dict = JsonConvert.DeserializeObject<ChildDictionary>(json);
Console.WriteLine("Name: " + dict.Name);
foreach (var kvp in dict)
{
Console.WriteLine("Employee Id: " + kvp.Key.Id);
Console.WriteLine("Employee Name: " + kvp.Key.Name);
Console.WriteLine("Employee Hire Date: " + kvp.Key.HireDate);
Console.WriteLine("Amount: " + kvp.Value);
Console.WriteLine();
}
}
}
[JsonConverter(typeof(ComplexDictionaryConverter<Employee, double>))]
public class ChildDictionary : Dictionary<Employee, double>
{
public string Name { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime HireDate { get; set; }
}
Output:
{
"Name": "Roster",
"KVPs": [
{
"Key": {
"Id": 22,
"Name": "Joe",
"HireDate": "2012-04-17T00:00:00"
},
"Value": 1923.07
},
{
"Key": {
"Id": 45,
"Name": "Fred",
"HireDate": "2010-08-22T00:00:00"
},
"Value": 1415.25
}
]
}
Name: Roster
Employee Id: 22
Employee Name: Joe
Employee Hire Date: 4/17/2012 12:00:00 AM
Amount: 1923.07
Employee Id: 45
Employee Name: Fred
Employee Hire Date: 8/22/2010 12:00:00 AM
Amount: 1415.25
Fiddle: https://dotnetfiddle.net/fTfoIk

Serialize object with json.net with nested dictionaries

I'm a little lost with serializing a JSON object, I'm trying to serialize the Item class into a JSON
class Item
{
public Dictionary<string, object> components = new Dictionary<string, object> { };
public string mixins { get; set; }
public string type { get; set; }
}
class Components
{
public Dictionary<string, object> unit_info = new Dictionary<string, object> { };
public Dictionary<object, object> generateUnitInfo()
{
unit_info.Add("bla", "blubb");
unit_info.Add("blubb", "bla");
return unit_info;
}
}
My JSON should look like this
{
"components": {
"unit_info" : {
"bla": "blubb",
"blubb": "bla",
},
}
}
Any hint would be helpful, thanks in advance
EDIT: thats the code that I have so far
Component c = new Component();
Item item = new Item();
item.type = CBItemType.SelectedItem.ToString();
item.mixins = "test mixins";
item.components.Add(c.unit_info, c.generateUnitInfo());
string json = JsonConvert.SerializeObject(item, Formatting.Indented);
and thats what I get
{
"mixins": "test mixins",
"type": "entity",
"components": {
"(Collection)": {
"bla": "blubb",
"blubb": "bla"
}
},
"entity_data": {}
}
The generateUnitInfo method adds 2 k/v pairs to the unit_info, I want instead of (Collection) unit_info
You can use anonymous objects (Json.Net)
var obj = new { components = new { unit_info = new { bla="blubb", blubb="bla" } } };
var json = JsonConvert.SerializeObject(obj);
Built-in JavaScriptSerializer would give the same result too
var json = new JavaScriptSerializer().Serialize(obj);
PS: If using dictionary is a must you can use it too
var obj = new Dictionary<string, object>()
{
{
"components", new Dictionary<string,object>()
{
{
"unit_info" , new Dictionary<string,object>()
{
{ "bla", "blubb" }, {"blubb", "bla" }
}
}
}
}
};
Both serializers will return your expected json.

Categories

Resources