Serialize dictionary as array (of key value pairs) - c#

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);
}
}

Related

How can I parse json to a C# object (Class)?

I have JSON data that I need to parse from C# object.
this is JSON Example.
{
"types":
[
[
"tour_type",
[
["groups",1],
["individual",2]
]
]
]
}
Here are my C# classes that are meant to contain that data:
using System;
using Newtonsoft.Json;
namespace JsonDeserializationTest
{
[JsonProperty("types")]
public class Types
{
[JsonProperty]
public List<Type> Values {get;set;}
}
public class Type
{
[JsonProperty]
public string Key {get;set;}
[JsonProperty]
public List<Dictionary<string, int>> Values { get; set; }
}
}
It's not working now.
How can I fix it?
Use the JsonSerializer (System.Text.Json) object.
Code:
YourClass obj = JsonSerializer.Deserialize<YourClass>(jsonString);
Your json has a list of list of the object... but you are declaring only List of the object.
public class Types
{
[JsonProperty("types")]
public List<List<object>> Values { get; set; }
// ------ UPDATE: This can only be list of list of 'object' ------- \\
}
Also, you are using the JsonProperty on the class, which is not where that normally goes. You want to use that on the property of the class.
UPDATE:
You cannot use List<List<Type>> for the json you are getting, it can only be List<List<object>>. You have to use object because it can either be a string or a List<List<string>>. After you update your Types class, you can successfully deserialize the json above.
var obj = JsonConvert.DeserializeObject<Types>(json);
and based on your json definition, you can access tour_type by using the following code
types.Values.First()[0].ToString()
// output: tour_type
List<List<string>> data = JsonConvert.DeserializeObject<List<List<string>>>(types.Values.First()[1].ToString())
// data[0]
[0]: "groups"
[1]: "1"
// data[1]
[0]: "individual"
[1]: "2"
Since both of the items in the types are objects, you will either have to convert them to string or a list of list of strings or whatever object they actually are.
The JSON payload in the provided example is formatted quite strangely, especially since it contains seemingly unnecessary array nesting. A payload like this usually includes more nested objects (rather than a bunch of nested arrays). Additionally, it has a list of (string, int) pairs, which is semantically very similar to a Dictionary<string, int>, but the payload doesn't lend itself to that. It would be helpful to know where it is coming from (what context) to understand how it might change.
The example JSON brings up a few questions (that you may want to ask yourself):
Can the "types" array contain multiple entries (at its immediate nesting)?
Can the "tour_type" key name appear after the array of string, int pairs? Is it possible for an entry where no such name exists?
What other elements can exist in the arrays within "tour_type"?
Is it guaranteed that the most nested array will contain just a single (string, int) pair?
Similarly, it is hard to understand what the example C# class is trying to encapsulate. Is List<Dictionary<string, int>> necessary?
All that said, here's a solution using the built-in System.Text.Json library, that could work for you. You could write something similar using Newtonsoft.Json, if necessary. The solution assumes:
We can't change the JSON payload (and that the third party API response will always returns something that is structurally similar to the example)
We can only make minimal changes to the C# class object provided in the example
The solution creates and a JsonConverter<T> that uses the low-level Utf8JsonReader to manually parse and create the custom object. This is required since nested "[" are being used to delineate what should be objects rather than "{". The converter is then registered by annotating the class with the attribute. Now, simply call JsonSerializer.Deserialize, passing in the JSON payload.
public class Tours
{
[JsonPropertyName("types")]
public List<UserType> Types { get; set; }
}
// Annotate the type to register the converter to use
[JsonConverter(typeof(CustomUserTypeConverter))]
public class UserType
{
public string Key { get; set; }
public Dictionary<string, int> Values { get; set; }
}
// This will use the low-level reader to build up the UserType
public class CustomUserTypeConverter : JsonConverter<UserType>
{
// Extra structural validation was done for invalid/incomplete JSON
// which might be too strict or incorrect and hence might require adjustments.
public override UserType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var result = new UserType();
if (!reader.Read())
{
throw new JsonException("Incomplete JSON.");
}
if (reader.TokenType != JsonTokenType.EndArray)
{
result.Key = reader.GetString();
ReadAndValidate(ref reader, JsonTokenType.StartArray);
int depthSnapshot = reader.CurrentDepth;
var values = new Dictionary<string, int>();
do
{
reader.Read();
if (reader.TokenType != JsonTokenType.StartArray && reader.TokenType != JsonTokenType.EndArray)
{
throw new JsonException($"Invalid JSON payload. Expected Start or End Array. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}.");
}
if (reader.CurrentDepth <= depthSnapshot)
{
break;
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndArray)
{
string key = reader.GetString();
reader.Read();
int value = reader.GetInt32();
values.Add(key, value);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
}
} while (true);
ReadAndValidate(ref reader, JsonTokenType.EndArray);
result.Values = values;
}
return result;
}
private void ReadAndValidate(ref Utf8JsonReader reader, JsonTokenType expectedTokenType)
{
bool readNext = reader.Read();
if (!readNext || reader.TokenType != expectedTokenType)
{
string message = readNext ?
$"Invalid JSON payload. TokenType: {reader.TokenType}, Depth: {reader.CurrentDepth}, Expected: {expectedTokenType}" :
$"Incomplete JSON. Expected: {expectedTokenType}";
throw new JsonException(message);
}
}
// Implement this method if you need to Serialize (i.e. write) the object
// back to JSON
public override void Write(Utf8JsonWriter writer, UserType value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Here's how you would use the above converter to serialize the JSON string provided in the example, along with how to access the values.
public static Tours ParseJson(string json)
{
Tours tours = JsonSerializer.Deserialize<Tours>(json);
return tours;
}
public static void AccessValues(Tours tours)
{
foreach (UserType data in tours.Types)
{
string typeName = data.Key; // "tour_type"
foreach (KeyValuePair<string, int> pairs in data.Values)
{
string key = pairs.Key; // "groups" or "individual
int value = pairs.Value; // 1 or 2
}
}
}
For what it's worth, Visual Studio suggests the following C# class structure for the example JSON (which is similar to what #Jawad suggested):
public class Rootobject
{
public object[][] types { get; set; }
}
Hope that helps.
I couldn't figure out your JSON so I created an example with verified JSON.
Try this:
JSON:
{
"Items": [
{
"Name": "tour",
"Attributes": [
{
"Name": "groups",
"Value": 1
},
{
"Name": "individual",
"Value": 2
}
]
},
{
"Name": "demo",
"Attributes": [
{
"Name": "this is demo",
"Value": 3
},
{
"Name": "design pattern",
"Value": 99
}
]
}
]
}
Types foo = JsonSerializer.Deserialize<Types>(jsonString);
public class TypeAttribute
{
public string Name { get; set; }
public int Value { get; set; }
}
public class Type
{
private readonly ICollection<TypeAttribute> _attributes;
public Type()
{
_attributes = new Collection<TypeAttribute>();
}
public void AddAttributes(IEnumerable<TypeAttribute> attrs)
{
foreach(TypeAttribute ta in attrs)
{
_attributes.Add(ta);
}
}
public string Name { get; set; }
public IEnumerable<TypeAttribute> Attributes
{
get { return _attributes; }
set
{
foreach(TypeAttribute ta in value)
{
_attributes.Add(ta);
}
}
}
}
public class Types
{
ICollection<Type> _items;
public Types()
{
_items = new Collection<Type>();
}
public void AddItems(IEnumerable<Type> tps)
{
foreach (Type t in tps)
{
_items.Add(t);
}
}
public IEnumerable<Type> Items
{
get { return _items; }
set
{
foreach (Type t in value)
{
_items.Add(t);
}
}
}
}

C# MongoDB complex class serialization

I'm working with C# MongoDB driver and I have this rather complex JSON struct to save:
{
"name" : "value",
"age": 1,
"isFemale": true,
"Hobbies" : {
//All data within the "Hobbies" node is dynamic
//and may change from one item to another.
"stringItem" : "value",
"intItem" : 0.0,
"listOfItems" : [
{ "field" : 1696.0 }
],
"intArray" : [ 566.0, 1200.0 ]
},
"Collection" : [
//All data within the "Collection" node is dynamic
//and may change from one item to another.
{
"field" : "string",
"FieldTypeId" : 2.0,
"array" : [
{ "value" : "1024x1000" }
]
}
]
}
I have this class that represents the above object:
public class MyClass
{
public string Name;
public int Age;
public bool IsFemale;
public Dictionary<string, object> Hobbies;
public List<Dictionary<string, object>> Collection;
}
Here is what I get saved for the "Hobbies" section:
"Hobbies": {
"stringItem": "value",
"intItem": 1,
"listOfItems": {
"_t": "Newtonsoft.Json.Linq.JArray, Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed",
"_v": [{
"_t": "JObject",
"_v": [{
"_t": "JProperty",
"_v": [{
"_t": "JValue",
"_v": []
}]
},
{
"_t": "JProperty",
"_v": [{
"_t": "JValue",
"_v": []
}]
}]
}]
}
}
My guess is that I should write a custom serializer for that class but I couldn't find any useful example in the MONGODB .NET DRIVER Manual
I tried to start and implement something like this:
public class MyClassSerializer : SerializerBase<MyClass>, IBsonDocumentSerializer
{
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, MyClass value)
{
//What to do here???
base.Serialize(context, args, value);
}
public override MyClass Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
//What to do here???
return base.Deserialize(context, args);
}
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
{
switch (memberName)
{
case "Name":
serializationInfo = new BsonSerializationInfo(memberName, new StringSerializer(), typeof(string));
return true;
case "Age":
serializationInfo = new BsonSerializationInfo(memberName, new Int16Serializer(), typeof(string));
return true;
case "IsFemale":
serializationInfo = new BsonSerializationInfo(memberName, new BooleanSerializer(), typeof(string));
return true;
case "Hobbies":
serializationInfo = new BsonSerializationInfo(memberName, new TupleSerializer<string, object>(), typeof(string));
return true;
case "Collection":
serializationInfo = new BsonSerializationInfo(memberName, new ArraySerializer<Dictionary<string, object>>(), typeof(string));
return true;
default:
serializationInfo = null;
return false;
}
}
}
But I couldn't find anything to do with this. It is not fully implemented and I read that I need to register the serializer somewhere, but where...?
A simple way for dealing with this issue and not have to create additional properties, is registering a custom serializer.
ComplexTypeSerializer.cs
namespace MyNamespace.MongoDB.Serializers
{
public class ComplexTypeSerializer : SerializerBase<object>
{
public override object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var serializer = BsonSerializer.LookupSerializer(typeof(BsonDocument));
var document = serializer.Deserialize(context, args);
var bsonDocument = document.ToBsonDocument();
var result = BsonExtensionMethods.ToJson(bsonDocument);
return JsonConvert.DeserializeObject<IDictionary<string, object>>(result);
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
{
var jsonDocument = JsonConvert.SerializeObject(value);
var bsonDocument = BsonSerializer.Deserialize<BsonDocument>(jsonDocument);
var serializer = BsonSerializer.LookupSerializer(typeof(BsonDocument));
serializer.Serialize(context, bsonDocument.AsBsonValue);
}
}
}
And register it as per http://mongodb.github.io/mongo-csharp-driver/2.3/reference/bson/serialization/
BsonSerializer.RegisterSerializer(typeof(IDictionary<string, object>), new ComplexTypeSerializer());
or
BsonSerializer.RegisterSerializer(typeof(IList<IDictionary<string, object>>), new ComplexTypeSerializer());
The code below was tested with a simple IDictionary<string, object>, not sure if it would work with IList<IDictionary<string, object>>, however if it's not supported, you can create another custom serializer to support it.
Found how to save the data to MongoDB here: Dictionary-to-BsonDocument conversion omitting _t field and extended it a bit so I thought to share the full solution.
Step #1:
In my class, I declared 2 members for each value:
// For the Hobbies object type:
[BsonIgnore] //ignore this value in MongoDB
public Dictionary<string, object> Hobbies { get; set; }
[JsonIgnore] //ignore this value in the response on Get requests
[BsonElement(elementName: "Hobbies")]
public BsonDocument HobbiesBson { get; set; }
/*********************************************************************/
// For the Collection object type:
[BsonIgnore] //ignore this value in MongoDB
public List<Dictionary<string, object>> Collection { get; set; }
[JsonIgnore] //ignore this value in the response on Get requests
[BsonElement(elementName: "Collection")]
public BsonArray CollectionBson { get; set; }
Step #2
In my WebAPI controller method for Post
[HttpPost]
public override async Task<IActionResult> Post([FromBody] Person person)
{
var jsonDoc = JsonConvert.SerializeObject(person.Hobbies);
person.HobbiesBson = BsonSerializer.Deserialize<BsonDocument>(jsonDoc);
jsonDoc = JsonConvert.SerializeObject(person.Collection);
person.CollectionBson = BsonSerializer.Deserialize<BsonArray>(jsonDoc);
//save
}
Step #3
In my Get request I deserialize it back like this:
[HttpGet("{id?}")]
public override async Task<IActionResult> Get(string id = null)
{
var people = //get data from mongoDB
foreach (var person in people)
{
var bsonDoc = BsonExtensionMethods.ToJson(person.HobbiesBson);
person.Hobbies = JsonConvert.DeserializeObject<Dictionary<string, object>>(bsonDoc);
bsonDoc = BsonExtensionMethods.ToJson(person.CollectionBson);
person.Collection = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(bsonDoc);bsonDoc);
}
return Ok(people);
}
This solved my issue and I hope it can help others as well :-)

C# Json To Dictionary of Objects

I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.
This is some example Json:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job"
}
]
}
This is the code I used to deserialize:
var json = File.ReadAllText(#"c:\temp\job.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?
This is a modified version of #DanielKeogh's code. It works well.
class Program
{
static void Main(string[] args)
{
var json = File.ReadAllText(#"c:\temp\job3.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
RecurseDeserialize(result);
}
private static void RecurseDeserialize(Dictionary<string, object> result)
{
//Iterate throgh key/value pairs
foreach (var keyValuePair in result.ToArray())
{
//Check to see if Newtonsoft thinks this is a JArray
var jarray = keyValuePair.Value as JArray;
if (jarray != null)
{
//We have a JArray
//Convert JArray back to json and deserialize to a list of dictionaries
var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
//Set the result as the dictionary
result[keyValuePair.Key] = dictionaries;
//Iterate throught the dictionaries
foreach (var dictionary in dictionaries)
{
//Recurse
RecurseDeserialize(dictionary);
}
}
}
}
}
This modified Json shows how deep it goes:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job",
"JobNoteNotes": [
{
"Something": 1,
"Something2": "Test"
}
]
}
]
}
The result ends three dictionaries deep so that I can get at the "Something" value by key.
This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)
Dictionary<string, object> RecursiveDeserialize(string json)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var pair in result.ToArray())
{
if(IsJson(pair.Value))
{
result[pair.Key] = RecursiveDeserialize(pair.Value);
}
}
return result;
}
Using this object for json string
public class JobNote
{
public string NoteText { get; set; }
public string NoteType { get; set; }
}
public class ListJob
{
public int JobNumber { get; set; }
public object Asset { get; set; }
public List<JobNote> JobNotes { get; set; }
}
Then you can deserialize it

Newtonsoft.Json Deserialize Collection using "Add"

Sorry for my English
When object has some "side effect" to change the property,
Result not same as before deserialize,
I know shouldn't serialize object has "side effect",
But i need walk around as this moment
Json:
{
"SerializeBar": "bar1,bar2",
"Bar": [
"bar1",
"bar2" <-- only has 2 item
]
}
Deserialize:
var deserialzedObject = JsonConvert.DeserializeObject<Model>(json);
deserialzedObject.Bar.ForEach(x => Console.WriteLine(x));
//bar1
//bar2
//bar1 <-- why JsonConvert using "Add", not replace WHOLE List
//bar2
Console.WriteLine(deserialzedObject.SerializeBar);
//bar1,bar2,bar1,bar2
Model:
class Model
{
public string SerializeBar
{
get { return string.Join(",", Bar); }
set { Bar = value.Split(',').ToList(); }
}
public List<string> Bar { get; set; }
}
PS: not accept answer attribute [JsonIgnore], or reorder Property position because real case is to complex
You have to set JsonSerializerSettings
JsonConvert.DeserializeObject<Model>(json, new JsonSerializerSettings()
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});

Create a list of Key Value pairs using Key = Enums Name and Value = Enums value using best technique

In C# 4.0 - 5.0 how do you create a list of key value pairs where the key is the enums name's value and the value is the enums name value.
Also in C# how do you create a list of key value pairs where the key is the enums name and the value is the attributes EnumName name.
I want to have two seperate list based on the enum's name in order to retrieve the value or the human readable name from the attribtue.
example:
public enum QATypes
{
[EnumMember(Value = "Black Box")]
BlackBox = 10,
[EnumMember(Value = "Integration Tests")]
IntegrationTests = 20,
[EnumMember(Value = "Acceptance Testing")]
AcceptanceTesting= 30,
...
}
and the first list would look like this for values:
{{ key:"BlackBox", value:10 }, { key:"IntegrationTests ", value:20 },{ key:"AcceptanceTesting", value:30 },...}
and the second list would look like this for enumsmember:
{{ key:"BlackBox", value:"Black Box"}, { key:"IntegrationTests ", value:"Integration Tests"},{ key:"AcceptanceTesting", value:"Acceptance Testing"},...}
You may use Reflection. Try this (assuming that all the enum members have an EnumMemberAttribute):
var qatype = typeof(QATypes);
var names = qatype.GetEnumNames();
var values = qatype.GetEnumValues().Cast<int>().ToList();
var nameValues = names.Select(n =>
qatype.GetMember(n)[0]
.CustomAttributes.First()
.NamedArguments[0].TypedValue
.Value)
.ToList();
var valuesList = names.Select((n, index) =>
new { key = n, value = values[index] })
.ToList();
var nameValuesList = names.Select((n, index) =>
new { key = n, value = nameValues[index] })
.ToList();
Further on, since your question seems to be JSON - related, if you want to serialize the lists in the JSON format, you may use Json.NET:
var valuesJson = JsonConvert.SerializeObject(valuesList);
Console.WriteLine(valuesJson);
Output:
[
{
"key": "BlackBox",
"value": 10
},
{
"key": "IntegrationTests",
"value": 20
},
{
"key": "AcceptanceTesting",
"value": 30
}
]
And
var nameValuesJson = JsonConvert.SerializeObject(nameValuesList);
Console.WriteLine(nameValuesJson);
Which outputs:
[
{
"key": "BlackBox",
"value": "Black Box"
},
{
"key": "IntegrationTests",
"value": "Integration Tests"
},
{
"key": "AcceptanceTesting",
"value": "Acceptance Testing"
}
]
Some remarks about the returned order and exception handling
Relying on the order of the items returned by the GetEnumValues and GetEnumNames seems not to be a potential issue, as both these methods rely internally on the same method:
[SecuritySafeCritical]
private static void GetCachedValuesAndNames
(RuntimeType enumType,
out ulong[] values,
out string[] names,
bool getValues,
bool getNames)
and ultimately on this method:
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static extern void GetEnumValuesAndNames
(RuntimeTypeHandle enumType,
ObjectHandleOnStack values,
ObjectHandleOnStack names,
bool getValues,
bool getNames);
The code snippet I provided doesn't cover all exceptions, it relies on the sample data provided in the original question. For example, it doesn't check for the existence of the EnumMemberAttribute or it's Value property. But this can be easily changed with a little amount of effort.
Enum.GetValues is dangerous when you have enum members with same values but different names, like:
enum { A, B = A, C }
You should use Enum.GetNames and then get the corresponding values.
I have this helper class to deal with similar things:
public static class Enum<T> where T : struct, IComparable, IFormattable, IConvertible
{
public static IEnumerable<T> GetValues()
{
return (T[])Enum.GetValues(typeof(T));
}
public static IEnumerable<string> GetNames()
{
return Enum.GetNames(typeof(T));
}
public static T Parse(string #enum)
{
return (T)Enum.Parse(typeof(T), #enum);
}
public static S GetAttribute<S>(string #enum) where S : Attribute
{
return (S)typeof(T).GetMember(#enum)[0]
.GetCustomAttributes(typeof(S), false)
.SingleOrDefault();
}
}
This is fully generic, so you need a bit more typing to call. Now I can do:
var first = Enum<QATypes>.GetNames().ToDictionary(x => x, x => (int)Enum<QATypes>.Parse(x));
var second = first.ToDictionary(x => x.Key, x => Enum<QATypes>.GetAttribute<EnumMemberAttribute>(x.Key).Value);
You can create your own extension method in a suitable namespace where you need not pass the enum attribute type argument, to ease calling.
Also you can make the helper class non-static which will help you with type-inference better and thus make calling code shorter. Should look like:
var helper = new Enum<QATypes>();
var first = helper.GetNames().ToDictionary(x => x, x => (int)helper.Parse(x));
var second = first.ToDictionary(x => x.Key, x => helper.GetAttribute<EnumMemberAttribute>(x.Key).Value);
For the first question:
var list = new List<KeyValuePair<string,int>>();
foreach(var e in Enum.GetValues(typeof(QATypes)))
{
list.Add(new KeyValuePair<string,int>(e.ToString(), (int)e));
}
for the second question:
var list = new List<KeyValuePair<string,string>>();
foreach(var e in typeof(QATypes).GetFields())
{
var attribute = e.GetCustomAttributes(typeof(EnumMemberAttribute))
.FirstOrDefault() as EnumMemberAttribute;
if(attribute != null)
{
list.Add(new KeyValuePair<string,string>(e.Name, attribute.Value));
}
}

Categories

Resources