I want to use JSON.Net to serialize a complex C#.net object into JSON. The class structure is flexible (completely under my control), but JSON is needed in a specific format to communicate with another application. I require JSON in the following format, but need assistance composing the classes and nested collections:
{
"Record_1": [ { "key_1": "value_1", "key_N": "value_N"}, { "key_1": "value_1", "key_N": "value_N"}],
"Record_2": [ { "key_1": "value_1" } ],
"Record_N": [ { "key_1": "value_1" } , { "key_N":"value_N"}],
… Other properties (simple string key value pairs)
}
There will be 1 or more Record properties, the value of each Record is an array of objects.
The Record property names are not known (I have a list of possibilities, but do not want to hardcode them into the application).
There will be 1 or more objects in each array.
The objects in the array will have 1 or more string key value pairs. Again, property names are not known at compile time.
Since the Record properties were unknown, I tried using a dictionary of objects (Dictionary<string,object>) annotated with [JsonExtensionData]. The value object in the dictionary was another collection. However, that results in string properties with object values:
"Record_1": { key_1: [ value_1, value_2, value_N] }, "Record_N": { key_1: [value_1] }
Close, but not correct, the value of Record has to be an array.
Next, I tried using a list of lists of objects, but that didn't work either because the variable names of the lists are returned as the property names:
"RecordSet": [
{
"Records": [
{
"Key_1": "Value_1"
}
]
},
{
"Records": [
{
"Key_1": "Value_1",
}
]
}
]
So this does result in an array objects, but contents of the object are not in the correct form.
I believe I need some combination of nested collections in my class to output the records in the required JSON format, but I don't know which. Hopefully someone else has encountered this situation before.
BTW: Currently, I'm directly writing to the JSON object to get the structure required. I'm hoping that there will be some performance increase in refactoring for serialization. I'm not looking to just add complexity so I can say I'm following object oriented coding principles. There will never be a need for deserialization of this JSON back into an object.
Updated answer (see comments for why):
The structure to match your JSON is Dictionary<string, List<Dictionary<string, string>>>. You'll have to figure out how to fill the property names you want for the first Dictionary key. One suggestion for that is to have an interface, something like ICanSerializeToSpecificJSONFormat, and then have all your classes implement that interface, with a method to convert the desired properties into this structure.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace SomeNamespace
{
class Program
{
static void Main()
{
var d1 = new Dictionary<string, string>
{
{ "key_1", "value_1" }
};
var d2 = new Dictionary<string, string>(d1)
{
{ "key_2", "value_2" }
};
var objectToSerialize = new Dictionary<string, List<Dictionary<string, string>>>
{
{ "Record_1", new List<Dictionary<string, string>> { d1, d2 } },
{ "Record_2", new List<Dictionary<string, string>> { d1 } }
};
string recordsAsJson = JsonConvert.SerializeObject(objectToSerialize);
Console.WriteLine(recordsAsJson);
// prints:
// {"Record_1":[{"key_1":"value_1"},{"key_1":"value_1","key_2":"value_2"}],"Record_2":[{"key_1":"value_1"}]}
}
}
}
Original answer:
You were close. You tried Dictionary<> and List<List<>>, but you need List<Dictionary<>>. Here's a fully compiling example for you:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace SomeNamespace
{
class RecordSet
{
public List<Dictionary<string, string>> Record_1 { get; set; }
public List<Dictionary<string, string>> Record_2 { get; set; }
}
class Program
{
static void Main()
{
var d1 = new Dictionary<string, string>
{
{ "key_1", "value_1" }
};
var d2 = new Dictionary<string, string>(d1)
{
{ "key_2", "value_2" }
};
var records = new RecordSet
{
Record_1 = new List<Dictionary<string, string>> { d1, d2 },
Record_2 = new List<Dictionary<string, string>> { d1 }
};
string recordsAsJson = JsonConvert.SerializeObject(records);
Console.WriteLine(recordsAsJson);
// prints:
// {"Record_1":[{"key_1":"value_1"},{"key_1":"value_1","key_2":"value_2"}],"Record_2":[{"key_1":"value_1"}]}
}
}
}
Hopefully you can use this as a guide for creating your own RecordSet class (e.g. modifying to add the 'other properties' you mention).
Related
I've a json that have objects like this
"SeasonalInfoBySeasonID": {
"aa8e0d2d-d29d-4c03-84f0-b0bda96a9fe1": {
"ID": "aa8e0d2d-d29d-4c03-84f0-b0bda96a9fe1",
"A": "1",
"B": 5,
},
"6f95fb92-5eb2-4fe4-ae01-dc54d810c8a5": {
"ID": "6f95fb92-5eb2-4fe4-ae01-dc54d810c8a5",
"A": "1",
"B": 5,
}, .....
}
the objects of SeasonalInfoBySeasonID are clearly all of the same data structure, but unfortunately they aren't even listed in an array, and instead of listing them one by one in seasoninfobyseasonid model I'm using doing this:
public class SeasonalInfoBySeasonID
{
private IDictionary<string, object> _additionalProperties = new Dictionary<string, object>();
[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties; }
set { _additionalProperties = value; }
}
}
works great, but I was wondering is it possible to de-serialize such an object with multiple objects of the "same type", to an array directly?
You don't need the class at all.
You can just declare the property on the root object like this
public Dictionary <string, YourType> SeasonalInfoBySeasonID {get;set;} = new Dictionary <string, YourType>();
Then each key will be aa8e0d2d-d29d-4c03-84f0-b0bda96a9fe1 etc., and the objects will be the inner type.
I have a C# class that receives a JSON string, and deserialises it into a dynamic.
json = #"{
"Id": "97dc4a96-43cf-48bd-9358-8f33e910594e",
"RepId": 90037,
"Something": true,
"SomethingElse": "abcdefg",
"Thing_1_of_MaybeDozens": 55
}";
dynamic jsonData = JsonConvert.DeserializeObject(json);
I can't deserialse into a class because while each JSON string will always have two known data elements (Id, and RepId), the rest of the string may have many elements that I do not know ahead of time their names, or how many of them there are.
I can always ask for jsonData.Id, or jsonData.RepId, but I do not know how many other elements there may be, nor how to refer to them.
I need something similar to JavaScript's Object.Keys(myObject).
Anyone knows how to do similar in C# with a Newtonsoft Deserialised JSON string?
You can use Reflection:
public Dictionary<string, object> DynamicToDictionary(dynamic obj)
{
var dict = new Dictionary<string, object>();
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(obj))
{
object obj2 = pd.GetValue(obj);
dict.Add(pd.Name, obj2);
}
return dict;
}
Now you may use the dictionary's TryGetValue(), and ContainsKey().
try to use Newtonsoft.Json extensions
var data = JsonConvert.DeserializeObject<Data>(json);
list of keys
List<string> additionalKeys=data.AdditionalData.Select(ad => ad.Key).ToList();
output
["Something","SomethingElse","Thing_1_of_MaybeDozens"]
how to use
var id = data.Id; //97dc4a96-43cf-48bd-9358-8f33e910594e
var somethingElse = data.AdditionalData["SomethingElse"]; // abcdefg
class
public class Data
{
// normal deserialization
public string Id { get; set; }
public long RepId { get; set; }
//additional data
[JsonExtensionData]
public Dictionary<string, object> AdditionalData {get;set;}
}
Problem
Heres my JSON object:
{
"1000":{
"id": "23445",
"latlon": "6780"
},
"1001":{
"id": "23454",
"latlon": "6784"
},
"1002":{
"id": "23245",
"latlon": "6180"
},
"1003":{
"id": "12345",
"latlon": "6740"
}
}
As you can see the property names are pure integers (1000, 1001, 1002, 1003)
I can't declare class variable names in integar and run System.json.Serialization.
Therefore I need to load the JSON file into:
public static Dictionary<int, NodeDetail> NodeInfo;
public class NodeDetail {
public ulong id;
public int latlon;
//Generic class serializer
//Generic class deserializer
}
Is there a library function for this? or do I have to parse the JSON string from ground up?
ps. I'm new to C#(coming from C++)
Newtonsoft.Json. You can pull it from NuGet.
Simple example:
public static T DeserializeJson<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
and then
Dictionary<string, object> foo = DeserializeJson<Dictionary<string, object>>(" ... insert JSON here ... ");
EDIT: given the structure you mention in your question, you may want to deserialize into a nested dictionary:
Dictionary<int, Dictionary<long, int>> foo = DeserializeJson<Dictionary<int, Dictionary<long, int>>>(" ... insert JSON here ...");
There's also JObject (in the same Newtonsoft.Json library) which is very helpful for deserializing to something uniform that can be used.
I'm utilizing Dictionary. After .insert() there are "_t" and "_v". Two posts here talked about serialization converting to JSON first then BSON. I'm using MongoDB's driver v2.4.3,
mCollection.InsertOne(x);
IMongoCollection<myDoc> mCollection = Db.GetCollection<myDoc>("whatever");
If I do JSON-to-BSON, it complains about can't convert BsonDocument to myDoc. Switching to IMongoCollection<BsonDocument> mCollection = Db.GetCollection<BsonDocument>("whatever"); still get _t and _v.
How to avoid _t and _v?
Here is my code of data type and utilization:
public class myObjForDictionary
{
//...
}
public class myDoc
{
// ... some other elements, then Dictionary
public Dictionary<string, object> myDictionary { get; set; }
}
// to instantiate the
class myClass
{
// define MongoDB connection, etc.
// instantiate myDoc and populate data
var x = new myDoc
{
//...
myDictionary = new Dictionary<string, object>
{
{ "type", "something" },
{ "Vendor", new object[0] },
{ "obj1", //data for myObjForDictionary
}
};
}
}
I think you're looking for the DictionarySerializationOption... which gives you a couple of different options out of the box to determine how your dictionary gets serialized.
You'll need to save some information (_t) of what object the deserializer needs to create when deserializing the object, if not it won't know what to create from the BSON.
Alternatively, you could change the Dictionary<string, object> to Dictionary<string, BsonDocument> and deal with the BsonDocuments directly within your code. This will be very similar to how you use JObject within Newtonsoft.Json.
It also happens when the passed model to mongo is not exactly the type that is defined on the model.
In this case, mongo will add _t as it identifies that you expect to read the inheritor's class type, and not the type that is defined on the model.
For example:
public class ParentClass { }
public class ChildClass : ParentClass { }
public class ModelToInsert
{
public ParentClass property { get; set; }
}
...
// ChildClass is passed instead of ParentClass
collection.InsertOne(new ModelToInsert { property = new ChildClass() });
Lets assume I have the following JSON Object:
{
"Key": "\"QTuY+0m31w2QiZGl4h+W8w==\"",
"Value":
[
"addgroup",
"viewgroup",
"editgroup"
]
}
How can I deserialize the Value part in a C# string[]?
The problem is, string[] is not the only type I have in the Value part, it can be everything, so I need to use dynamic.
I am using JSON.net and when I use the JsonConvert.DeserializeObject method I get a runtime binder exception.
This is the method I use to deserialize:
public async Task<Tuple<string, dynamic>> ReadCachingEntryAsync(string uri) {
var data = tools.ReadEntry(Path.Combine(cacheFolder, uri.Replace("/", "")));
var res = new Tuple<string, dynamic>(string.Empty, null);
if (data != null) {
var dat = JsonConvert.DeserializeObject<KeyValuePair<string, dynamic>>(UTF8Encoding.UTF8.GetString(data));
res = new Tuple<string, dynamic>(dat.Key, dat.Value);
}
return res;
}
The tools.ReadEntry(data) returns a byte[] containing the data, from the binary formatted file.
Here is the class KeyValuePair<K, V>
[Serializable]
internal struct KeyValuePair<K, V> {
public K Key { get; set; }
public V Value { get; set; }
}
You can check if it's a JArray and implement special handling. E.g.
byte[] data = Encoding.UTF8.GetBytes(#"{""Key"": ""Something"", ""Value"": [""test"", ""test 2"", ""test 3""]}");
var dat = JsonConvert.DeserializeObject<KeyValuePair<string, dynamic>>(UTF8Encoding.UTF8.GetString(data));
var value = dat.Value;
if (value is JArray)
{
var arrayType = value[0].Value.GetType().MakeArrayType();
value = value.ToObject(arrayType);
}
var res = new Tuple<string, dynamic>(dat.Key, value);
Just call DeserializeObject<T> replacing T with the type you intend to deserialize the input string into and json.NET will take care of the rest.
string[] json = JsonConvert.DeserializeObject<string[]>(inputString);
EDIT:
Ok, so assuming all of your json is in the form of the example posted in EDIT 3 then waht you really want is;
public class KVPair
{
public string Key { get; set; }
public string[] Value { get; set; }
}
KVPair pair = JsonConvert.DeserializeObject<KVPair>(UTF8Encoding.UTF8.GetString(data));
Now this is fairly static but I'm not sure you actually need dynamic. If you do then you'll want the Value property to be of type object or dynamic. However, if you go that route then you will unfortunately have to deal with this unknown type. I'm really not knowledgeable on the dynamic type so I can't comment on how to deal with it later on but if say for example, the array could contain, ints, floats or strings and you made Value of type object you would have inspect the array later on, determine the underlying type and cast each of the items to that type before you could do anything useful with them. Hope that helps. If you're lucky you'll just be able to use the code I posted above as it's far simpler and is actually type safe.