Kartik Javali
Kartik Javali

Reputation: 327

System.Text.Json Serialize List<Dictionary<string, string>> dynamically

I am developing a test using SpecFlow and I am obtaining test arguments via ScenarioContext object.

I want to Serialize an object of type List<Dictionary<string, string>>, to produce the output as below:

"TestCaseArguments":[
    {"First Number":-50, "Second Number": 70, "Expected Result":20},
    {"First Number":30, "Second Number": -30, "Expected Result":0},
    {"First Number":-60, "Second Number": 30, "Expected Result":-30}
],

The count of test arguments such as "First Number", etc. in the following example, is varying in each test case and also the names of keys are not fixed, they change for each test case.

Note: TestCaseArguments is defined as follows inside a class:

public IList<Dictionary<string, string>> TestCaseArguments { get; set; }

In my c# .net code, I am able to get each argument as a key-value pair inside a myList as follows:

List<Dictionary<string, string>> myList = new List<Dictionary<string, string>>();

for (i = 0; i < keyList.Length; i++)
{
    myList.Add(new Dictionary<string, string> { [keyList[i]] = valueList[i] });
}

As I iterate over myList and add each item to the TestCaseArguments List, I get something like this below:

"TestCaseArguments":[
    {"First Number":-50}, {"Second Number": 70}, {"Expected Result":20},
    {"First Number":30}, {"Second Number": -30}, {"Expected Result":0},
    {"First Number":-60}, {"Second Number": 30}, {"Expected Result":-30}]  
 

So, that is exactly the problem. As you may have noticed closely, I am getting {"First Number":-50}, {"Second Number": 70}, {"Expected Result":20}, instead of {"First Number":-50, "Second Number": 70, "Expected Result":20}.

I could try changing my JSON structure, however I hope there a simple way to achieve this desired structure with some expert coding advice.

Upvotes: 0

Views: 1397

Answers (1)

Monsieur Merso
Monsieur Merso

Reputation: 2176

Assume that we have following setup for the question:

Example class for serialization:

class ExampleClass
{
    public IList<Dictionary<string, string>> TestCaseArguments { get; set; }
}

And it is being initialized with following data:

var firstRow = new Dictionary<string, string>();
firstRow["First Number"] = "-50";
firstRow["Second Number"] = "70";
firstRow["Expected Result"] = "20";

var secondRow = new Dictionary<string, string>();
secondRow["First Number"] = "30";
secondRow["Second Number"] = "-30";
secondRow["Expected Result"] = "0";

var thirdRow = new Dictionary<string, string>();
thirdRow["First Number"] = "30";
thirdRow["Second Number"] = "-30";
thirdRow["Expected Result"] = "0";

var initialList = new List<Dictionary<string, string>> {
    firstRow, secondRow, thirdRow,
};

var classToSerialize = new ExampleClass { TestCaseArguments = initialList };

There are two approaches (that I'm aware of) how we can serialize it to a requested structure:

var writerOptions = new JsonWriterOptions { Indented = true, };
// Console.OpenStandardOutput is just for example here
// to dump json writer output to the console, you can write to memory or
// to a file
using var stream = Console.OpenStandardOutput();
using var writer = new Utf8JsonWriter(stream, writerOptions);

writer.WriteStartObject();
writer.WriteStartArray("TestCaseArguments");
foreach (Dictionary<string, string> testArguments in initialList)
{
    writer.WriteStartObject();
    foreach(var keyValue in testArguments)
    {
        writer.WritePropertyName(keyValue.Key);
        // convert value to number and write it
        writer.WriteNumberValue(Convert.ToInt32(keyValue.Value));
    }
    writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();

Console output will look like this:

{
  "TestCaseArguments": [
    {
      "First Number": -50,
      "Second Number": 70,
      "Expected Result": 20
    },
    {
      "First Number": 30,
      "Second Number": -30,
      "Expected Result": 0
    },
    {
      "First Number": 30,
      "Second Number": -30,
      "Expected Result": 0
    }
  ]
}
var options = new JsonSerializerOptions { WriteIndented = true, };
var jsonString = JsonSerializer.Serialize(classToSerialize, options);

jsonString will look like this:

{
   "TestCaseArguments": [
     { "First Number": "-50", "Second Number": "70", "Expected Result": "20" },
     { "First Number": "30", "Second Number": "-30", "Expected Result": "0" },
     { "First Number": "30", "Second Number": "-30", "Expected Result": "0" }
   ]
}

Note: JsonSerializer used on Dictionary<string, string> will treat dictionary keys as strings so numbers will be "quoted" in the output, to avoid that you can convert dictionary to Dictionary<string, int> or try to write custom converter with help of JsonConverterAttribute

Upvotes: 1

Related Questions