replacing keys in json with numbers which corresponds to enums - c#

I'm making a json out of a class in c# and for making the json smaller i want to replace the string keys with numbers which correspond to a number of enums.
an example would be a json the looks like this:
{
"initdata":
{
"cell": {
"apn": "n",
"userName": "n",
"password": "n",
"number": "n"
},
"wifi": {
"mode": "AP",
"ssid": "m",
"password": ","
},
"ports": {
"p1": "k",
"p2": "y",
"p3": "5"
}
}
and enums that looks like this :
public enum celle { apn, userName, password, number };
public enum wifie { mode, ssid, password };
public enum portse { p1, p2, p3 };
public enum initdatae { cell , wifi , ports};
public enum settingse { initdata, timers }
and then I want the json to look like this :
{
"0":
{
"0": {
"0": "n",
"1": "n",
"2": "n",
"3": "n"
},
"1": {
"0": "AP",
"1": "m",
"2": ","
},
"2": {
"0": "k",
"1": "y",
"2": "5"
}
}
Thanks

If you are using Json.Net, for renaming the properties names, take a look here for exemple (there is plenty on Stack Overflow): Overwrite Json property name in c#
And for the values, I suggest you assign an int to each enum :
public enum celle
{
apn = 0,
userName = 1,
password = 2,
number = 3
};
And then you cast the enum to an int to get the value before converting it to json:
int celleValue = (int)celle.userName;
So, in the end your class will look like something like this:
public class ClassToConvert()
{
[JsonIgnore] // this property will not be on the json
public celle celleValue {get;set;}
[JsonProperty(PropertyName = "c1")]
public int celleShort
{
get
{
return (int)this.celleValue;
}
}
}
public enum celle
{
apn = 0,
userName = 1,
password = 2,
number = 3
};

If you don't have POCO's defined for your input JSON, you may rename your properties as follows:
deserialize the input JSON into an ExpandoObject
rename the ExpandoObject's properties as desired
serialize the ExpandoObject back to JSON:
public static void Main(string[] args)
{
var json = #"{
""initdata"": {
""cell"": {
""apn"": ""n"",
""userName"": ""n"",
""password"": ""n"",
""number"": ""n""
},
""wifi"": {
""mode"": ""AP"",
""ssid"": ""m"",
""password1"": "",""
},
""ports"": {
""p1"": ""k"",
""p2"": ""y"",
""p3"": ""5""
}
}
}";
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
PrepareEnumIds();
RenameEnumProps(obj as ExpandoObject);
var shortJson = JsonConvert.SerializeObject(obj, Formatting.Indented);
Console.WriteLine(shortJson);
}
public static void RenameEnumProps(ExpandoObject expando)
{
var expandoDict = expando as IDictionary<string, object>;
var props = new List<string>(expandoDict.Keys);
for (var i = props.Count - 1; i >= 0; i--)
{
var child = expandoDict[props[i]];
if (child is ExpandoObject)
RenameEnumProps(child as ExpandoObject);
if (enumIds.ContainsKey(props[i]))
{
expandoDict.Add(enumIds[props[i]], child);
expandoDict.Remove(props[i]);
}
}
}
static Dictionary<string, string> enumIds = new Dictionary<string, string>();
static void PrepareEnumIds()
{
foreach (var enumType in new[] { typeof(celle), typeof(wifie), typeof(portse), typeof(initdatae), typeof(settingse) })
foreach (var enumValue in Enum.GetValues(enumType))
enumIds.Add(enumValue.ToString(), ((int)enumValue).ToString());
}
public enum celle { apn, userName, password, number };
public enum wifie { mode, ssid, password1 };
public enum portse { p1, p2, p3 };
public enum initdatae { cell, wifi, ports };
public enum settingse { initdata, timers }
Demo: https://dotnetfiddle.net/IPqxEQ
Note: I've cheated a little bit by changing one of the passwords values to make all enum values unique, sorry. This made renaming implementation significantly easier. But this does not affect the main point of implementation. I'll provide a complete implementation when I'll have some more spare time.

Related

C# JSON Anonymous Type - Multiple properties with the same name

I am needing to produce this JSON string with C#:
{
"in0": {
"children": [
{
"ValueObjectDescriptor": {
"fields": [
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
],
"objectName": "Job",
"limit": 1,
"xpathFilter": "#openJob = 'true'"
}
}
]
}
}
Here is my code:
static string BuildJsonString()
{
var json = new
{
in0 = new
{
children = new
{
ValueObjectDescriptor = new
{
fields = new
{
FieldDescriptor = new
{
name = "length",
xpath = "#length",
},
FieldDescriptor = new
{
name = "height",
xpath = "#height",
},
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
}
}
}
};
var jsonFormatted = JsonConvert.SerializeObject(json, Newtonsoft.Json.Formatting.Indented);
return jsonFormatted.ToString();
The issue I am having is that the compiler doesn't like me using "FieldDescriptor" multiple times, I get the error "An anonymous type cannot have multiple properties with the same name".
I am very new to JSON, so any advice would be greatly appreciated.
Note that not only is having multiple fields with the same name invalid C# code, having duplicate keys in the same object is also invalid JSON. You can be sure that there is no JSON that would need you to write duplicate field names to serialise it.
The "FieldDescriptor" JSON keys are actually part of different JSON objects, and these JSON objects are all in a JSON array under the key "fields":
[
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
]
The [ ... ] denotes the array, and each pair of { ... } denotes a JSON object. So you should create an (implicitly typed) array of anonymous objects, each one with the FieldDescriptor property, rather than one object having all three of the proeperties:
fields = new[] // <--- create an array
{
new {
FieldDescriptor = new
{
name = "length",
xpath = "#length",
}},
new { // notice the new pairs of curly braces
FieldDescriptor = new
{
name = "height",
xpath = "#height",
}}, // here's the closing brace
new {
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
}},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
It looks like in the json "fields" is an array of objects where each object contains a "FieldDescriptor" field. In your C# code you are creating "fields" as an object with multiple fields not an array of objects.

Dictionary with tuples values returning null?

I have a class with a dictionary defined as a private member :
Dictionary<int, (string, string)> arenaIdToSetAndNumber = new Dictionary<int, (string, string)>()
{
{ 70506, ("c16", "337") },
{ 70507, ("c16", "340") },
{ 70508, ("c16", "343") },
{ 70509, ("c16", "346") },
{ 70510, ("c16", "349") },
};
While debugging, I get to an item corresponding to key 70506, I see this with 2 watches:
I try doing var test = arenaIdToSetAndNumber[c.grpId].Item1 and test is set to null just as seen in the second watch! I don't understand why
The debugger and the watcher are not able to infer what is Item1 from the indexer operator [], thus will give you null in the watch. But once you run the code, it will just work fine for reading purpose. For writing purpose instead, you need to take out the whole tuple, edit it and reinsert in the dictionary:
static void Main(string[] args)
{
Dictionary<int, (string, string)> arenaIdToSetAndNumber = new Dictionary<int, (string, string)>()
{
{ 70506, ("c16", "337") },
{ 70507, ("c16", "340") },
{ 70508, ("c16", "343") },
{ 70509, ("c16", "346") },
{ 70510, ("c16", "349") },
};
var myTuple = arenaIdToSetAndNumber[70509];
myTuple.Item1 = "c18";
arenaIdToSetAndNumber[70509] = myTuple;
//System.Console.WriteLine(arenaIdToSetAndNumber[70509].Item1); // This prints c18
}
Otherwise, in one line, just recreate the whole tuple:
arenaIdToSetAndNumber[70509] = ("c18", arenaIdToSetAndNumber[70509].Item2);
All of this because the ValueTuple is a struct. Similar question here
This does not use tuples but solves your problem. Since you want to read the values create an immutable class, use properties to retrive the values.
public class Contents
{
private readonly string leftValue;
private readonly string rightValue;
public Contents(string aLeftValue, string aRightValue)
{
leftValue = aLeftValue;
rightValue = aRightValue;
}
public string LeftValue => leftValue;
public string RightValue => rightValue;
}
Modify your code to use the new class.
Dictionary<int, Contents> arenaIdToSetAndNumber = new Dictionary<int, Contents>()
{
{ 70506, new Contents("c16", "337") },
{ 70507, new Contents("c16", "340") },
{ 70508, new Contents("c16", "343") },
{ 70509, new Contents("c16", "346") },
{ 70510, new Contents("c16", "349") },
};
And you can test it with this.
var content = arenaIdToSetAndNumber[70506];
string leftValue = content.LeftValue;
string rightValue = content.RightValue;
Hope this solves your problem.

how to replace the entire array using MongoDB c# driver

Say, the existing document in DB is like this:
{
"_id": "1",
"settings": {
"languages": [ "english", "french" ]
}
}
Now I want to update the document to this:
{
"_id": "1",
"settings": {
"languages": [ "korean", "german" ]
}
}
I tired this following code:
var lan = new List<string> { "finish", "russian", "korean" }
collection.UpdateOne(
Builders<MyObject>.Filter.Eq("_id", "1"),
Builders<MyObject>.Update.Set("settings.languages", lan));
But got following exception:
MongoDB.Bson.Serialization.Serializers.EnumerableInterfaceImplementerSerializer2[System.Collections.Generic.List1[System.String],System.String]' cannot be converted to type 'MongoDB.Bson.Serialization.IBsonSerializer`1[System.String]
I also tried using BsonArray to initialize the new languages array:
var bsonArray = new BsonArray
{
"korean",
"german"
};
collection.UpdateOne(
Builders<MyObject>.Filter.Eq("_id", "1"),
Builders<MyObject>.Update.Set("settings.languages", bsonArray));
The update could be executed without error, but the languages in document is changed to:
{
"_id": "1",
"settings": {
"languages": "[korean, german]"
}
}
It becomes "[ xx, xx ]", instead of [ "xx", "xx" ]. It is not what I expected.
You're getting that error message because you're using strongly typed builders on type MyObject which probably looks more or less like below:
public class MyObject
{
public string _id { get; set; }
public Settings settings { get; set; }
}
public class Settings
{
public string[] languages { get; set; }
}
So since you have a strongly typed Builder you are getting an exception because of the type mismatch between List and Array. Two ways to fix that:
Either use .ToArray() on list to get the same type as the one you have in your MyObject:
var lan = new List<string> { "finish", "russian", "korean" };
collection.UpdateOne(
Builders<MyObject2>.Filter.Eq("_id", "1"),
Builders<MyObject2>.Update.Set("settings.languages", lan.ToArray()));
or skip the type validation using BsonDocument class:
var collection = mydb.GetCollection<BsonDocument>("col");
var lan = new List<string> { "finish", "russian", "korean" };
collection.UpdateOne(
Builders<BsonDocument>.Filter.Eq("_id", "1"),
Builders<BsonDocument>.Update.Set("settings.languages", lan));

Deserialize a JSON file which has nested dictionaries

I have the following JSON file,
{
"NAME": {
"ABC": {
"Score": 2,
"violations": []
},
"DEF": {
"Score": 4,
"violations": []
},
"GHI": {
"Score": 6,
"violations": ["badform"]
}
}
I am trying to deserilaize this by creating a class but I am finding it very difficult to construct the class as I am either getting nulls or JSON.net crashing. Could anyone tell me the best way to construct the deserializer?
This should work (at least it works here if I add missing “}” to the end of JSON file):
using ParsedData = Dictionary<string, Dictionary<string, A>>;
class A {
public int Score;
public string[] violations;
}
var parsed = JsonConvert.DeserializeObject<ParsedData>(…);
Another version:
class A {
public int Score;
public string[] violations;
}
class B : Dictionary<string, A> {}
class C : Dictionary<string, B> {}
var parsed = JsonConvert.DeserializeObject<C>(…);

Serialize dictionary as array (of key value pairs)

Json.Net typically serializes a Dictionary<k,v> into a collection;
"MyDict": {
"Apples": {
"Taste": 1341181398,
"Title": "Granny Smith",
},
"Oranges": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
},
}
Which is great. And from looking around on SO it seems to be what most people want. However, in this particular case, I want to serialize between my Dictionary<k,v> and the Array format instead;
"MyDict": [
"k": "Apples",
"v": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
"k:": "Oranges",
"v:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
Is there an easy way to do this with my existing field type? Is there an attribute I can annotate for instance?
Another way to accomplish this is to use a custom ContractResolver. That way you do not have to subclass Dictionary<K,V> nor apply a transform each time you serialize, as suggested in other answers.
The following resolver will cause ALL dictionaries to be serialized as an array of objects with "Key" and "Value" properties:
class DictionaryAsArrayResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) ||
(i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
{
return base.CreateArrayContract(objectType);
}
return base.CreateContract(objectType);
}
}
To use the resolver, add it to your JsonSerializerSettings, then pass the settings to JsonConvert.SerializeObject() like this:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new DictionaryAsArrayResolver();
string json = JsonConvert.SerializeObject(obj, settings);
Here is a working demo.
Ah, it turns out this is as straightforward as I'd hoped. My Dictionary<k,v> is subclassed already and I found that I can annotate it with [JsonArrayAttribute]. That gives me exactly the format I need;
"MyDict": [
{
"Key": "Apples",
"Value": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
{
"Key:": "Oranges",
"Value:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
For this example, I'll use the dictonary:
var myDict = new Dictionary<string,string>() {
{"a","123"},
{"b","234"},
{"c","345"}
};
which serializes (with Newtonsoft.Json.JsonConvert.SerializeObject(myDict)) to:
{"a":"123","b":"234","c":"345"}
You could do a transform using LINQ to create an anonymous object, and serialize that:
var myDictTransformed = from key in myDict.Keys
select new { k = key, v = myDict[key] };
Or you could use a real object
class MyDictEntry
{
public string k { get; set; }
public string v { get; set; }
}
and either the above or the alternative LINQ syntax:
var myDictTransformed = myDict.Keys.AsEnumerable()
.Select(key => new MyDictEntry{
k = key,
v = myDict[key]
});
Either way, this serializes to:
[
{"k":"a", "v":"123"},
{"k":"b", "v":"234"},
{"k":"c", "v":"345"}
]
.NET Fiddle link: https://dotnetfiddle.net/LhisVW
The simplest solution I found is to convert your Dictionary<string, string> to a List<KeyValuePair<string, string>>. JSON.NET then converts your List into an array of objects with the form { Key: 'keyname', Value: 'value' }. This works well if you accept the required model change and don't want to subclass your Dictionary.
gregmac's answer was helpful, but didn't quite work. The following is the same idea... without the puns.
var dictionaryTransformed = dictionary.Select(item => item.Key).Select(i =>
new {Key = i, Value = dictionary[i] });
or of course
var dictionaryTransformed = dictionary.Select(item =>
new {item.Key, Value = dictionary[item.Key] });
Then to json
var json = (new JavaScriptSerializer()).Serialize(
new { Container = dictionaryTransformed.ToArray() } )
I'm not exactly sure why, but the custom ContractResolver by Brian Rogers listed above didn't work for me. It seemed to get into an endless loop somewhere internally. Possibly due to other parts of my json.net setup.
Anyway - this workaround did the trick for me.
public interface IStrongIdentifier
{
string StringValue { get; set; }
}
public class StrongIdentifierKeyedDictionaryWrapper<TKey, TValue> : Dictionary<string, TValue>
where TKey : IStrongIdentifier
{
public void Add(TKey key, TValue value)
{
base.Add(key.StringValue, value);
}
public void Remove(TKey key)
{
base.Remove(key.StringValue);
}
public TValue this[TKey index]
{
get => base[index.StringValue];
set => base[index.StringValue] = value;
}
public bool ContainsKey(TKey key)
{
return base.ContainsKey(key.StringValue);
}
}

Categories

Resources