I'm trying to parse huge json file to 2d array.
I can parse. But required memory is almost 10times.
My sample.json file has 100,000 rows, each with a different item.
If sample.json is 500MB this code need 5GB.
How can i reduce memory usage?
I use Newtonsoft.Json, .Net6.0
Read from json
static void Read()
{
List<Dictionary<string, string>> rows = new List<Dictionary<string, string>>();
string path = #"D:\small.json";
using (FileStream fsRead = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bsRead = new BufferedStream(fsRead))
using (StreamReader srRead = new StreamReader(bsRead))
{
string? line;
while ((line = srRead.ReadLine()) != null)
{
JObject jsonObject = JObject.Parse(line);
MakeRowData(jsonObject, out var row);
rows.Add(row);
}
}
}
Make row
private static void MakeRowData(JObject jsonData, out Dictionary<string, string> row)
{
Dictionary<string, string> output = new Dictionary<string, string>();
foreach (var item in jsonData)
{
int childSize = 0;
if (item.Value != null)
{
childSize = item.Value.Children().Count();
///if Item has child, explore deep
if (childSize > 0)
{
ExploreChild(item.Value, ref output);
}
///or not just add new item
else
{
string str = item.Value.ToString();
output[item.Key] = str ?? "";
}
}
}
row = output;
}
private static void ExploreChild(JToken jToken, ref Dictionary<string, string> row)
{
foreach (var item in jToken)
{
int childSize = item.Children().Count();
///if Item has child, explore deep
if (childSize > 0)
{
ExploreChild(item, ref row);
}
///or not just add new item
else
{
string path = jToken.Path.Replace('[', '(').Replace(']', ')');
string str = jToken.First.ToString();
row[path] = str?? "";
}
}
}
EDIT
Add Sample.json
It is set of json strings.
And Fields are not fixed.
Sample.json
{Field1:0,Field2:1,Field2:3}
{Field1:0,Field5:1,Field6:3}
{Field1:0,Field7:1,Field9:3}
{Field1:0,Field13:1,Field50:3,Field57:3}
...
You can try replacing the recursive exploring children with the iterative one. Something like this:
private static void MakeRowData(JObject jsonData, out Dictionary<string, string> row)
{
Dictionary<string, string> output = new Dictionary<string, string>();
foreach (var item in jsonData)
{
if (item.Value != null)
{
///if Item has child, explore deep
if (item.Value.HasValues)
{
var queue = new Queue<JToken>();
queue.Enqueue(item.Value);
while (queue.Any())
{
var currItem = queue.Dequeue();
if (currItem.HasValues)
{
foreach(var child in item)
queue.Enqueue(child);
}
else
{
// add item without children to row here
}
}
}
///or not just add new item
else
{
string str = item.Value.ToString();
output[item.Key] = str ?? "";
}
}
}
row = output;
}
Recursive calls, unless it is a tail recursion, keep the stack of a method they were called from. This can lead to extensive memory usage.
Related
I want to read the info1.txt file and want to write in another text file info2.txt in this manner.
Id Name Address DOB Phone
1 abcd efg 1/16/2021 987654323
2 hijkl mno 2/16/2021 678987652
The contents of the info1.txt file are as follows:
Id:1
Name:abcd
Address:efg
DOB:1/16/2021 3:31:22 PM
Phone:987654323
And the info2.txt would be like above table format that I mentioned,also want to remove "3:31:22 PM". The code block I developed to solve this problem is available below:
static void Main(string[] args)
{
FileStream fsRead = new FileStream("E:\\info1.txt", FileMode.Open, FileAccess.Read);
StreamReader srObj = new StreamReader(fsRead);
FileStream fsWrite = new FileStream("E:\\info2.txt", FileMode.Create, FileAccess.Write);
StreamWriter swObj = new StreamWriter(fsWrite);
while (srObj.Peek() > 0)
{
string str;
string[] strArray;
str = srObj.ReadLine();
str = str.Replace(" 3:31:22 PM", "");
strArray = str.Split(':');
if (strArray.Length > 1)
{
swObj.Write(strArray[1]);
swObj.Write(" ");
}
}
swObj.Close();
fsWrite.Close();
srObj.Close();
fsRead.Close();
Console.ReadKey();
}
I would parse the file into a list of dictionaries where each dictionary's keys are the columns.
First split the file lines into an array of strings. You can use File.ReadAllLines for that. Then send the array to this function that parses the lines.
public static List<Dictionary<string, string>> Parse(string [] lines)
{
List<Dictionary<string, string>> data = new List<Dictionary<string, string>>();
Dictionary<string, string> temp = new Dictionary<string, string>();
foreach (var line in lines) {
var parts = line.Split(new[] { ':' }, 2);
if (parts.Length == 2) {
temp[parts[0]] = parts[1];
}
else {
if (temp.Count > 0) data.Add(temp);
temp = new Dictionary<string, string>();
}
}
if (temp.Count > 0) data.Add(temp);
return data;
}
Then, make a function to write the list to a file.
public static void PrintTable(List<Dictionary<string, string>> users, TextWriter stream)
{
if (users.Count == 0) return;
// Print the header line
foreach(var pair in users[0]) {
stream.Write("{0,-12}", pair.Key);
}
stream.WriteLine();
foreach (var user in users) {
foreach(var pair in user) {
// Special handling for DOB
if (pair.Key == "DOB") stream.Write("{0,-12}", pair.Value.Split(' ')[0]);
else stream.Write("{0,-12}", pair.Value);
}
stream.WriteLine();
}
}
I am new here and actually very new to c#.
In a nutshell, I am using c# via Visual Studio, I am calling a data from a database and I want to save these data in a .csv file. The problem now is that I want to save these data on two columns at the same time.
My code do write them in a file but shifted not on the right rows.
Dictionary<string, string> elementNames = new Dictionary<string, string>();
Dictionary<string, string> elementTypes = new Dictionary<string, string>();
var nodes = webservice.nepService.GetAllElementsOfElementType(webservice.ext, "Busbar", ref elementNames, ref elementTypes);
Dictionary<string, string> nodeResults = new Dictionary<string, string>();
Dictionary<string, string> nodeResults1 = new Dictionary<string, string>();
foreach (var nodename in elementNames.Values)
{
var nodeRes = webservice.nepService.GetResultElementByName(webservice.ext, nodename, "Busbar", -1, "LoadFlow", null);
var Uvolt = GetXMLAttribute(nodeRes, "U");
nodeResults.Add(nodename, Uvolt);
var Upercentage = GetXMLAttribute(nodeRes, "Up");
nodeResults1.Add(nodename, Upercentage);
StringBuilder strBldr = new StringBuilder();
string outputFile = #"C:\Users\12.csv";
string separator = ",";
foreach (var res in nodeResults)
{
strBldr.AppendLine($"{res.Key}{separator}{res.Value}");
}
foreach (var res1 in nodeResults1)
{
strBldr.AppendLine($"{separator}{separator}{res1.Value}");
}
File.WriteAllText(outputFile, strBldr.ToString());
}
this is the output of the previous code:
https://ibb.co/T4trQC3
I want these shifted values to move up beside the other values like that:
https://ibb.co/4S25v0h
Thank you
if you look to the code you are using AppendLine
strBldr.AppendLine($"{separator}{separator}{res1.Value}");
and if you want to append on same line just use Append
strBldr.Append($"{separator}{separator}{res1.Value}");
EDITED:
in linq you can use Zip function to zip to lists
// using System.Linq;
var results = Results.Zip(Results1, (firstList, secondList) => firstList.Key + "," + firstList.Value + "," + secondList.Value);
Edit Full example
public static IDictionary<string, string> Results { get; set; }
public static IDictionary<string, string> Results1 { get; set; }
private static void Main(string[] args)
{
StringBuilder strBldr = new StringBuilder();
string outputFile = #"D:\12.csv";
Results = new Dictionary<string, string>()
{
{"N1", "20"},
{"N2", "0.399992"},
{"N3", "0.369442"},
{"N4", "0.369976"}
};
Results1 = new Dictionary<string, string>()
{
{"N1", "100"},
{"N2", "99.9805"},
{"N3", "92.36053"},
{"N4", "92.49407"}
};
IEnumerable<string> results = Results.Zip(Results1,
(firstList, secondList) => firstList.Key + "," + firstList.Value + "," + secondList.Value);
foreach (string res1 in results)
{
strBldr.AppendLine(res1);
}
File.WriteAllText(outputFile, strBldr.ToString());
}
for faster code you can try this
HashSet<Tuple<string, string, string>> values = new HashSet<Tuple<string, string, string>>();
var nodes = webservice.nepService.GetAllElementsOfElementType(webservice.ext, "Busbar", ref elementNames, ref elementTypes);
foreach (var nodename in elementNames.Values)
{
var nodeRes = webservice.nepService.GetResultElementByName(webservice.ext, nodename, "Busbar", -1, "LoadFlow", null);
var Uvolt = GetXMLAttribute(nodeRes, "U");
var Upercentage = GetXMLAttribute(nodeRes, "Up");
values.Add(Tuple.Create(nodename, Uvolt, Upercentage));
}
var output = string.Join("\n", values.ToList().Select(tuple => $"{tuple.Item1},{tuple.Item2},{tuple.Item3}").ToList());
string outputFile = #"C:\Users\12.csv";
File.WriteAllText(outputFile, output);
if the rowCount for Results and Results1 are same and the keys are in the same order, try:
for (int i = 0; i < Results.Count; i++)
strBldr.AppendLine($"{Results[i].Key}{separator}{Results[i].Value}{separator}{Results1[i].Value}");
Or, if the rows are not in the same order, try:
foreach (var res in Results)
strBldr.AppendLine($"{res.Key}{separator}{res.Value}{separator}{Results1.Single(x => x.Key == res.Key).Value}");
I have a sparsely populated excel file I want to extract two columns into a dictionary in C#. I have tried the following. This fails when it reads the blank lines. Is there a cleaner way to achieve the same. I don't care about any other values here. Just a mapping of AR ID to AR Type would do.
public class Table
{
private Dictionary<string, string> _ARID_ARTypeValues = new Dictionary<string, string>();
private string _arId;
public Table(string arId)
{
_arId = arId;
}
public void AddValue(string key, string value)
{
_ARID_ARTypeValues.Add(key, value);
}
}
public static IDictionary ParseCsvFile(StreamReader reader)
{
Dictionary<string, Table> tables = new Dictionary<string, Table>();
// First line contains column names.
var columnNames = reader.ReadLine().Split(',');
for (int i = 1; i < columnNames.Length; ++i)
{
var columnName = columnNames[i];
var ntable = new Table(columnName);
if ((columnName == "AR ID") || (columnName == "AR Type"))
{
tables.Add(columnName, ntable);
}
}
var line = reader.ReadLine();
while (line != null)
{
var columns = line.Split(',');
for (int j = 1; j < columns.Length; ++j)
{
var table = tables[columnNames[j]];
table.AddValue(columns[0], columns[j]);
}
line = reader.ReadLine();
}
return tables;
}
I would just use a CSV library, like CsvHelper and read the csv file with that.
Dictionary<string, string> arIdToArTypeMapping = new Dictionary<string, string>();
using (var sr = File.OpenText("test.csv"))
{
var csvConfiguration = new CsvConfiguration
{
SkipEmptyRecords = true
};
using (var csvReader = new CsvReader(sr, csvConfiguration))
{
while (csvReader.Read())
{
string arId = csvReader.GetField("AR ID");
string arType = csvReader.GetField("AR Type");
if (!string.IsNullOrEmpty(arId) && !string.IsNullOrEmpty(arType))
{
arIdToArTypeMapping.Add(arId, arType);
}
}
}
}
You can use Cinchoo ETL - an open source library, to read the csv and convert them to dictionary as simple as with few lines of code shown below
using (var parser = new ChoCSVReader("Dict1.csv")
.WithField("AR_ID", 7)
.WithField("AR_TYPE", 8)
.WithFirstLineHeader(true)
.Configure(c => c.IgnoreEmptyLine = true)
)
{
var dict = parser.ToDictionary(item => item.AR_ID, item => item.AR_TYPE);
foreach (var kvp in dict)
Console.WriteLine(kvp.Key + " " + kvp.Value);
}
Hope this helps.
Disclaimer: I'm the author of this library.
I have the below code:
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
}
}
ticketStoreNew = ticketStore;
return output;
}`
It takes a dictionary, ticketStore, checks to see if it has any new elements not in ticketStoreNew and puts them in the output dictionary. Then, ticketStoreNew is set to ticketStore until ticketStore is updated with another method and this method is ran again.
However, when I include the line ticketStoreNew = ticketStore, the program returns an empty dictionary. It looks like the method is not executing sequentially and this is running prior to the for loop.
I really just need to return any new items added to the ticketStore dictionary.
EDIT
Below is the code for getting ticketStore:
public void UpdateTickets(string inputXml)
{
// If no new tickets exit
if (inputXml.Trim() == "") { return; }
//xmlString = inputXml;
// Load XML into an enumerable
XElement xelement = XElement.Parse(inputXml);
IEnumerable<XElement> xml = xelement.Elements();
foreach (var item in xml)
{
if (item.Name == "incident")
{
int id;
// If ID can be converted to INT
if (Int32.TryParse(item.Element("id").Value, out id))
{
// If ticket is not already in store create ticket and populate data
if (!ticketStore.ContainsKey(id))
{
Ticket ticket = new Ticket();
ticket.id = id;
ticket.number = Int32.Parse(item.Element("number").Value);
ticket.title = item.Element("name").Value;
ticket.description = item.Element("description").Value;
ticketStore.Add(id, ticket);
}
}
}
}
}
}
The tickets are all based on getting XML from the Samanage API.
If another method updates ticketStore then the assignment is the problem. It doesn't copy the contents of ticketStore to ticketStoreNew it sets the reference ticketStoreNew to point to the same instance as ticketStore. Thus they are the same object and always have the same contents. Try creating a new Dictionary to copy the items:
ticketStoreNew = new Dictionary<int, Ticket>(ticketStore);
Try this code:
private Dictionary<int, Ticket> ticketStoreNew =
new Dictionary<int, Ticket>(); // add this line
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
ticketStoreNew.Add(item.Key, item.Value); // add this line
}
}
//ticketStoreNew = ticketStore; remove this line
return output;
}
I need some kind of structure to add, remove and read (get) some values for some settings.
In PHP I would create sth. like:
["root"]["Audio"]["DuplexMode"] = full;
["root"]["AudioSource"]["A0"]["AudioEncoding"] = g711;
["root"]["Network"]["Interface"]["I0"]["Active"]["MACAddress"] = 00:40:8C:9A:1F:56;
and access it like this:
DuplexMode = ["root"]["Audio"]["DuplexMode"];
AudioEncoding = ["root"]["AudioSource"]["A0"]["AudioEncoding"];
MACAddress = ["root"]["Network"]["Interface"]["I0"]["Active"]["MACAddress"];
...
or
foreach (x in ["root"]["AudioSource"]["A0"]) {...}
should give me all the settings under root.AudioSource.A0.
But this seams not to be possible in C#. (Or at least I don't know how...)
Here is the list of settings which is a quite long string I get from a network-camera. (There are not always the same settings for each device.)
root.Audio.DuplexMode=full
root.Audio.MaxListeners=20
root.Audio.ReceiverBuffer=120
root.Audio.ReceiverTimeout=1000
root.Audio.NbrOfConfigs=1
root.Audio.DSCP=0
root.Audio.A0.Enabled=yes
root.Audio.A0.HTTPMessageType=singlepart
root.Audio.A0.Name=
root.Audio.A0.Source=0
root.Audio.A0.AlarmLevel=50
root.Audio.A0.AlarmResolution=50
root.Audio.A0.AlarmLowLimit=10
root.Audio.A0.AlarmHighLimit=6500
root.AudioSource.NbrOfSources=1
root.AudioSource.A0.Name=Audio
root.AudioSource.A0.AudioEncoding=g711
root.AudioSource.A0.InputType=mic
root.AudioSource.A0.InputGain=auto
root.AudioSource.A0.OutputGain=0
root.AudioSource.A0.SampleRate=8000
root.AudioSource.A0.HWSampleRate=16000
root.AudioSource.A0.BitRate=64000
root.AudioSource.A0.CustomReg=
root.AudioSource.A0.AudioSupport=yes
root.Bandwidth.Limit=0
root.Brand.Brand=AXIS
root.Brand.ProdFullName=AXIS M1031-W Network Camera
root.Brand.ProdShortName=AXIS M1031-W
root.Brand.ProdNbr=M1031-W
root.Brand.ProdType=Network Camera
root.Brand.WebURL=http://www.axis.com/
root.Event.E0.Name=NightLight
root.Event.E0.Type=T
root.Event.E0.Enabled=yes
root.Event.E0.Priority=1
root.Event.E0.Image=0
root.Event.E0.HWInputs=1
root.Event.E0.SWInput=
root.Event.E0.Weekdays=1111111
root.Event.E0.Starttime=19:00
root.Event.E0.Duration=12:00
root.Event.E0.MinimumTriggerInterval=00:00:00
root.Event.E0.MinimumTriggerTimePeriod=00:00:00
root.Event.E0.ImageURLSettingsEnabled=no
root.Event.E0.ImageURLSettings=
root.Event.E0.IncludePreTrigger=no
root.Event.E0.PreTriggerDuration=
root.Event.E0.PreTriggerDurationUnit=s
root.Event.E0.IncludePostTrigger=no
root.Event.E0.PostTriggerDuration=
root.Event.E0.PostTriggerDurationUnit=s
root.Event.E0.IncludeBestEffort=no
root.Event.E0.BestEffortDuration=0
root.Event.E0.BestEffortDurationUnit=s
root.Event.E0.IncludeAudio=no
root.Event.E0.MPEGPreTriggerDuration=5
root.Event.E0.MPEGPostTriggerDuration=2
root.Event.E0.VideoFormat=
root.Event.E0.FrameRate=
root.Event.E0.Actions.A0.Type=N
root.Event.E0.Actions.A0.Protocol=Light
root.Event.E0.Actions.A0.Order=0
root.Event.E0.Actions.A0.Light=1
root.Event.E0.Actions.A0.Activate=100
root.Event.E0.Actions.A0.Inactivate=-0
root.Event.E0.Actions.A0.Duration=30
root.Event.E0.Actions.A0.Unit=s
root.HTTPS.Certificate=none
root.HTTPS.Enabled=no
root.HTTPS.Port=443
root.Image.TriggerDataEnabled=yes
root.Image.ReferrersEnabled=no
root.Image.Referrers=
root.Image.MaxViewers=20
root.Image.MotionDetection=yes
root.Image.NbrOfConfigs=1
root.Image.RFCCompliantMultipartEnabled=yes
root.Image.PrivacyMaskType=mask_windows
root.Image.OverlayPath=/etc/overlays/axis(128x44).ovl
root.Image.DateFormat=YYYY-MM-DD
root.Image.OwnDateFormat=%F
root.Image.OwnDateFormatEnabled=no
root.Image.TimeFormat=24
root.Image.OwnTimeFormat=%T
root.Image.OwnTimeFormatEnabled=no
root.Image.TimeResolution=1
root.Image.I0.Name=
root.Image.I0.Source=0
root.Image.I0.Appearance.Resolution=640x480
root.Image.I0.Appearance.Compression=30
root.Image.I0.Appearance.Rotation=0
root.Image.I0.Appearance.MirrorEnabled=no
root.Image.I0.Appearance.ColorEnabled=yes
root.Image.I0.MPEG.ConfigHeaderInterval=1
root.Image.I0.MPEG.UserDataEnabled=yes
root.Image.I0.MPEG.UserDataInterval=1
root.Image.I0.MPEG.ICount=1
root.Image.I0.MPEG.PCount=14
root.Image.I0.MPEG.Complexity=50
root.Image.I0.MPEG.ResyncMarkerEnabled=yes
root.Image.I0.MPEG.H264.PSEnabled=no
root.Image.I0.Overlay.Enabled=no
root.Image.I0.Overlay.XPos=0
root.Image.I0.Overlay.YPos=0
root.Image.I0.Overlay.MaskWindows.Color=black
root.Image.I0.Overlay.MaskWindows.M0.Enabled=no
root.Image.I0.Overlay.MaskWindows.M0.XPos=303
root.Image.I0.Overlay.MaskWindows.M0.YPos=2
root.Image.I0.Overlay.MaskWindows.M0.Width=160
root.Image.I0.Overlay.MaskWindows.M0.Height=48
root.Image.I0.Overlay.MaskWindows.M0.Name=Mask%200
root.Image.I0.Overlay.MaskWindows.M1.Enabled=no
root.Image.I0.Overlay.MaskWindows.M1.XPos=124
root.Image.I0.Overlay.MaskWindows.M1.YPos=4
root.Image.I0.Overlay.MaskWindows.M1.Width=148
root.Image.I0.Overlay.MaskWindows.M1.Height=56
root.Image.I0.Overlay.MaskWindows.M1.Name=Mask%201
root.Image.I0.Overlay.MaskWindows.M2.Enabled=no
root.Image.I0.Overlay.MaskWindows.M2.XPos=583
root.Image.I0.Overlay.MaskWindows.M2.YPos=52
root.Image.I0.Overlay.MaskWindows.M2.Width=54
root.Image.I0.Overlay.MaskWindows.M2.Height=113
root.Image.I0.Overlay.MaskWindows.M2.Name=Mask%202
root.Image.I0.RateControl.Mode=vbr
root.Image.I0.RateControl.Priority=framerate
root.Image.I0.RateControl.TargetBitrate=0
root.Image.I0.RateControl.MaxFPS=30
root.Image.I0.RateControl.MinFPS=1
root.Image.I0.RateControl.MaxCompression=100
root.Image.I0.RateControl.MinCompression=0
root.Image.I0.RateControl.MaxBitrate=0
root.Image.I0.SizeControl.MaxFrameSize=0
root.Image.I0.SizeControl.DiffFrameSize=0
root.Image.I0.Stream.Duration=0
root.Image.I0.Stream.NbrOfFrames=0
root.Image.I0.Stream.FPS=15
root.Image.I0.Text.DateEnabled=no
root.Image.I0.Text.ClockEnabled=no
root.Image.I0.Text.TextEnabled=no
root.Image.I0.Text.String=ET1
root.Image.I0.Text.Position=top
root.Image.I0.TriggerData.IOEnabled=yes
root.Image.I0.TriggerData.AudioEnabled=yes
root.Image.I0.TriggerData.TamperingEnabled=yes
root.Image.I0.TriggerData.MotionDetectionEnabled=yes
root.Image.I0.TriggerData.MotionLevelEnabled=no
root.Image.I0.TriggerData.UserTriggers=
root.ImageSource.NbrOfSources=1
root.ImageSource.MotionDetection=no
root.ImageSource.I0.Name=Camera
root.ImageSource.I0.Sensor.WhiteBalance=auto
root.ImageSource.I0.Sensor.Exposure=flickerfree50
root.ImageSource.I0.Sensor.MaxExposureTime=10000
root.ImageSource.I0.Sensor.MinExposureTime=10000
root.ImageSource.I0.Sensor.MaxGain=100
root.ImageSource.I0.Sensor.MinGain=0
root.ImageSource.I0.Sensor.ExposureValue=59
root.ImageSource.I0.Sensor.ExposurePriority=50
root.ImageSource.I0.Sensor.ColorDesaturation=100
root.ImageSource.I0.Sensor.Brightness=51
root.ImageSource.I0.Sensor.Contrast=56
root.ImageSource.I0.Sensor.ColorLevel=60
root.ImageSource.I0.Sensor.Sharpness=64
root.ImageSource.I0.Sensor.BacklightCompensation=yes
root.ImageSource.I0.Sensor.DynamicPixelCorrection=yes
root.ImageSource.I0.Sensor.AspectRatio=
root.ImageSource.I0.Sensor.ExposureWindow=auto
root.ImageSource.I0.Sensor.CustomExposureWindow.Weight=100
root.Input.NbrOfInputs=1
root.IOPort.I0.Configurable=no
root.IOPort.I0.Direction=input
root.IOPort.I0.Input.Name=PIR sensor
root.IOPort.I0.Input.Trig=closed
root.Layout.DefaultVideoFormat=mjpeg
root.Layout.ShowVideoFormatDropDown=yes
root.Layout.DefaultStreamProfile=
root.Layout.ViewerIE=activex
root.Layout.ViewerOther=spush
root.Layout.SetupLinkEnabled=yes
root.Layout.SnapshotEnabled=no
root.Layout.PlainConfigEnabled=no
root.Layout.ShowAMCToolbar=yes
root.Layout.AMCRecordMedia=1
root.Layout.Axis=yes
root.Layout.OwnHomePageEnabled=no
root.Layout.OwnHomePagePath=dummy
root.Layout.MPEGInstallationEnabled=yes
root.Layout.H264InstallationEnabled=yes
root.Layout.AACInstallationEnabled=yes
root.Layout.EnableBasicSetup=yes
root.Layout.InstantReplayEnabled=no
root.Layout.InstantReplayTimeOffset=30
root.Layout.PlayAudioClipEnabled=yes
root.Layout.CustomLink.C0.Enabled=no
root.Layout.CustomLink.C0.Name=Custom link 1
root.Layout.CustomLink.C0.URL=http://
root.Layout.CustomLink.C0.Usage=cgi
root.Layout.CustomLink.C1.Enabled=no
root.Layout.CustomLink.C1.Name=Custom link 2
root.Layout.CustomLink.C1.URL=http://
root.Layout.CustomLink.C1.Usage=cgi
root.Layout.CustomLink.C2.Enabled=no
root.Layout.CustomLink.C2.Name=Custom link 3
root.Layout.CustomLink.C2.URL=http://
root.Layout.CustomLink.C2.Usage=cgi
root.Layout.CustomLink.C3.Enabled=no
root.Layout.CustomLink.C3.Name=Custom link 4
root.Layout.CustomLink.C3.URL=http://
root.Layout.CustomLink.C3.Usage=cgi
root.Layout.Trigger.T0.Enabled=no
root.LightControl.L0.Name=Light
root.LightControl.L0.DriverType=pwm
root.LightControl.L0.SourceType=whiteled
root.LightControl.L0.Driver=pa18
root.LightControl.L0.Button=actinact
root.LightControl.L0.Activate=-100
root.LightControl.L0.Inactivate=-0
root.LightControl.L0.PulseTime=0
root.Log.Access.MaxSize=40000
root.Log.Access.Critical=detailed
root.Log.Access.Warning=detailed
root.Log.Access.Informational=off
root.Log.System.MaxSize=40000
root.Log.System.Critical=on
root.Log.System.Warning=on
root.Log.System.Informational=on
root.MailLogd.ToEmail=
root.MailLogd.LogSendLevel=0
root.MediaClip.MaxGroups=10
root.MediaClip.M0.Name=Burglar_Alarm_Short
root.MediaClip.M0.Location=/etc/audioclips/Burglar_Alarm_Short_8bit_32kHz_mono_PCM-16bit-little.wav
root.MediaClip.M0.Type=audio
root.MediaClip.M6.Name=Camera clicks
root.MediaClip.M6.Location=/etc/audioclips/camera_clicks16k.au
root.MediaClip.M6.Type=audio
root.MediaClip.M7.Name=Pssst, Psst!
root.MediaClip.M7.Location=/etc/audioclips/pssst_psst16k.au
root.MediaClip.M7.Type=audio
root.MediaClip.M8.Name=Intruder
root.MediaClip.M8.Location=/etc/audioclips/intruder16k.au
root.MediaClip.M8.Type=audio
root.MediaClip.M9.Name=Dog barking
root.MediaClip.M9.Location=/etc/audioclips/dog_barking16k.au
root.MediaClip.M9.Type=audio
root.Motion.M0.Name=DefaultWindow
root.Motion.M0.ImageSource=0
root.Motion.M0.Left=0
root.Motion.M0.Right=9999
root.Motion.M0.Top=86
root.Motion.M0.Bottom=9998
root.Motion.M0.WindowType=include
root.Motion.M0.Sensitivity=95
root.Motion.M0.History=93
root.Motion.M0.ObjectSize=3
root.Network.Media=auto
root.Network.Autoneg=normal
root.Network.InterfaceSelectMode=auto
root.Network.HostName=axis-00408c9a1f56
root.Network.DNSServer1=192.168.69.251
root.Network.DNSServer2=0.0.0.0
root.Network.tcpECN=1
root.Network.Enabled=yes
root.Network.BootProto=none
root.Network.IPAddress=192.168.68.127
root.Network.SubnetMask=255.255.254.0
root.Network.Broadcast=192.168.69.255
root.Network.DefaultRouter=192.168.69.254
root.Network.DomainName=
root.Network.ARPPingIPAddress.Enabled=yes
root.Network.AxisNS.Enabled=yes
root.Network.AxisNS.LockButton=no
root.Network.AxisNS.UpdatePeriod=1
root.Network.AxisNS.CheckPeriod=10
root.Network.AxisNS.ServerList=www0.axiscam.net,www1.axiscam.net,195.60.68.29,195.60.68.30
root.Network.AxisNS.ServerPath=reg_cam.php
root.Network.AxisNS.ServerLink=www.axiscam.net
root.Network.AxisNS.CheckIPAddress=0.0.0.0
root.Network.AxisNS.CheckTTL=0
root.Network.Bonjour.Enabled=yes
root.Network.Bonjour.FriendlyName=AXIS M1031-W - 00408C9A1F56
root.Network.DHCP.StoreIPAddresseth0=no
root.Network.DHCP.StoreIPAddresseth1=no
root.Network.DHCP.IPCheckEnabled=yes
root.Network.DHCP.VendorClass=AXIS,Network Camera,M1031-W,5.20.1
root.Network.DHCP.Fqdn=
root.Network.DHCP.Timeout=0
root.Network.DHCP.Retries=20
root.Network.DNSUpdate.Enabled=no
root.Network.DNSUpdate.DNSName=Securitec
root.Network.DNSUpdate.TTL=30
root.Network.eth0.MACAddress=00:40:8C:9A:1F:56
root.Network.eth0.IPAddress=192.168.68.127
root.Network.eth0.SubnetMask=255.255.254.0
root.Network.eth0.Broadcast=192.168.69.255
root.Network.eth0.IPv6.IPAddresses=fe80::240:8cff:fe9a:1f56/64
root.Network.Filter.Enabled=no
root.Network.Filter.Input.Policy=allow
root.Network.Filter.Input.AcceptAddresses=
root.Network.Filter.Log.Enabled=yes
root.Network.FTP.Enabled=yes
root.Network.HTTP.AuthenticationPolicy=basic_digest
root.Network.HTTP.AuthenticationPolicySet=yes
root.Network.HTTP.AuthenticationWithQop=no
root.Network.Interface.I0.SystemDevice=eth0
root.Network.Interface.I0.Type=802.3
root.Network.Interface.I0.Active.MACAddress=00:40:8C:9A:1F:56
root.Network.Interface.I0.Active.Active=no
root.Network.Interface.I0.Active.IPAddress=
root.Network.Interface.I0.Active.SubnetMask=
root.Network.Interface.I0.Active.Broadcast=
root.Network.Interface.I0.Active.IPv6Addresses=
root.Network.Interface.I0.dot1x.Enabled=no
root.Network.Interface.I0.dot1x.EAPOLVersion=1
root.Network.Interface.I0.dot1x.Status=Stopped
root.Network.Interface.I0.dot1x.EAPTLS.Identity=xxx
root.Network.Interface.I0.dot1x.EAPTLS.PrivateKeyPassword=*****
root.Network.Interface.I0.Link.MTU=1500
root.Network.Interface.I0.Link.BootProto=none
root.Network.Interface.I0.Link.Media=auto
root.Network.Interface.I0.Link.Autoneg=normal
root.Network.Interface.I0.Link.IPv4Enabled=yes
root.Network.Interface.I0.Link.IPv6Enabled=no
root.Network.Interface.I0.Link.AcceptRA=yes
root.Network.Interface.I0.Link.DHCPv6=auto
root.Network.Interface.I0.Manual.IPAddress=192.168.68.127
root.Network.Interface.I0.Manual.SubnetMask=255.255.254.0
root.Network.Interface.I0.Manual.Broadcast=192.168.69.255
root.Network.Interface.I0.Manual.DefaultRouter=192.168.69.254
root.Network.Interface.I0.Manual.IPv6Address=
root.Network.Interface.I0.Manual.IPv6DefaultRouter=
root.Network.Interface.I0.ZeroConf.Enabled=yes
root.Network.Interface.I0.ZeroConf.IPAddress=
root.Network.Interface.I0.ZeroConf.SubnetMask=
root.Network.Interface.I1.SystemDevice=eth1
root.Network.Interface.I1.Type=802.11
root.Network.Interface.I1.Active.MACAddress=00:40:8C:9A:1F:56
root.Network.Interface.I1.Active.Active=yes
root.Network.Interface.I1.Active.IPAddress=192.168.68.127
root.Network.Interface.I1.Active.SubnetMask=255.255.254.0
root.Network.Interface.I1.Active.Broadcast=192.168.69.255
root.Network.Interface.I1.Active.IPv6Addresses=fe80::240:8cff:fe9a:1f56/64
root.Network.Interface.I1.dot1x.EAPOLVersion=1
root.Network.Interface.I1.dot1x.EAPTLS.Identity=
root.Network.Interface.I1.dot1x.EAPTLS.PrivateKeyPassword=*****
root.Network.Interface.I1.Link.MTU=1500
root.Network.Interface.I1.Link.BootProto=none
root.Network.Interface.I1.Link.IPv4Enabled=yes
root.Network.Interface.I1.Link.IPv6Enabled=no
root.Network.Interface.I1.Link.AcceptRA=yes
root.Network.Interface.I1.Link.DHCPv6=auto
root.Network.Interface.I1.Manual.IPAddress=192.168.68.127
root.Network.Interface.I1.Manual.SubnetMask=255.255.254.0
root.Network.Interface.I1.Manual.Broadcast=192.168.69.255
root.Network.Interface.I1.Manual.DefaultRouter=192.168.69.254
root.Network.Interface.I1.Manual.IPv6Address=
root.Network.Interface.I1.Manual.IPv6DefaultRouter=
root.Network.Interface.I1.ZeroConf.Enabled=yes
root.Network.Interface.I1.ZeroConf.IPAddress=169.254.248.199
root.Network.Interface.I1.ZeroConf.SubnetMask=255.255.0.0
root.Network.IPv6.Enabled=no
root.Network.IPv6.AcceptRA=yes
root.Network.IPv6.DHCPv6=auto
root.Network.IPv6.IPAddress=
root.Network.IPv6.DefaultRouter=
root.Network.QoS.Class1.Desc=AxisLiveVideo
root.Network.QoS.Class1.DSCP=0
root.Network.QoS.Class2.Desc=AxisLiveAudio
root.Network.QoS.Class2.DSCP=0
root.Network.QoS.Class3.Desc=AxisEventAlarm
root.Network.QoS.Class3.DSCP=0
root.Network.QoS.Class4.Desc=AxisManagement
root.Network.QoS.Class4.DSCP=0
root.Network.Resolver.NameServerList=192.168.69.251
root.Network.Resolver.ObtainFromDHCP=no
root.Network.Resolver.Search=
root.Network.Resolver.NameServer1=192.168.69.251
root.Network.Resolver.NameServer2=
root.Network.Routing.DefaultRouter=192.168.69.254
root.Network.Routing.IPv6.DefaultRouter=
root.Network.RTP.NbrOfRTPGroups=1
root.Network.RTP.StartPort=50000
root.Network.RTP.EndPort=50999
root.Network.RTP.VideoDSCP=0
root.Network.RTP.AudioDSCP=0
root.Network.RTP.R0.VideoAddress=239.226.31.86
root.Network.RTP.R0.VideoPort=0
root.Network.RTP.R0.AudioAddress=239.226.31.214
root.Network.RTP.R0.AudioPort=0
root.Network.RTP.R0.TTL=5
root.Network.RTSP.Enabled=yes
root.Network.RTSP.Port=554
root.Network.RTSP.Timeout=60
root.Network.RTSP.ProtViewer=password
root.Network.RTSP.AllowPathAsURL=yes
root.Network.RTSP.AuthenticateOverHTTP=no
root.Network.RTSP.AllowClientTransportSettings=no
root.Network.UPnP.Enabled=yes
root.Network.UPnP.FriendlyName=AXIS M1031-W - Office WiFi Cam
root.Network.UPnP.NATTraversal.Enabled=no
root.Network.UPnP.NATTraversal.Router=
root.Network.UPnP.NATTraversal.ExternalIPAddress=
root.Network.UPnP.NATTraversal.Active=no
root.Network.UPnP.NATTraversal.MinPort=32768
root.Network.UPnP.NATTraversal.MaxPort=65535
root.Network.VolatileHostName.ObtainFromDHCP=no
root.Network.VolatileHostName.HostName=axis-00408c9a1f56
root.Network.Wireless.ESSID=SecuriTec
root.Network.Wireless.MODE=managed
root.Network.Wireless.W0.Enabled=yes
root.Network.Wireless.W0.Method=WPA-PSK
root.Network.Wireless.W0.GenerationMethod=psk-phrase
root.Network.Wireless.W0.Passphrase=*****
root.Network.Wireless.W0.Key=
root.Network.Wireless.W1.Enabled=no
root.Network.Wireless.W1.Method=WPA-ENTERPRISE
root.Network.Wireless.W1.Dot1XMethod=EAPTLS
root.Network.Wireless.WEP.Enabled=no
root.Network.Wireless.WEP.KeyLength=128
root.Network.Wireless.WEP.GenerationMethod=manual
root.Network.Wireless.WEP.Passphrase=
root.Network.Wireless.WEP.ActiveKey=1
root.Network.Wireless.WEP.Key1=
root.Network.Wireless.WEP.Key2=
root.Network.Wireless.WEP.Key3=
root.Network.Wireless.WEP.Key4=
root.Network.Wireless.WEP.Authentication=open
root.Network.ZeroConf.Enabled=yes
root.Network.ZeroConf.IPAddress=169.254.248.199
root.Network.ZeroConf.SubnetMask=255.255.0.0
root.Notify.AtBoot=no
root.Notify.TextType=simple
root.Notify.TextField=Your text here
root.Notify.KnownIP=192.168.68.127
root.Notify.KnownIPV6=
root.Notify.FTP.Enabled=no
root.Notify.FTP.HostName=
root.Notify.FTP.UserName=
root.Notify.FTP.Password=
root.Notify.FTP.UploadPath=
root.Notify.FTP.PassiveMode=no
root.Notify.FTP.Port=21
root.Notify.HTTP.Enabled=no
root.Notify.HTTP.URL=http://
root.Notify.HTTP.Custom=
root.Notify.HTTP.UserName=
root.Notify.HTTP.Password=
root.Notify.HTTP.Proxy=
root.Notify.HTTP.ProxyPort=
root.Notify.HTTP.ProxyUser=
root.Notify.HTTP.ProxyPass=
root.Notify.SMTP.Enabled=no
root.Notify.SMTP.ToEmail=
root.Notify.SMTP.FromEmail=
root.Notify.SMTP.Subject=
root.Output.NbrOfOutputs=0
root.PIRSensor.Sensitivity=75
root.Properties.API.HTTP.Version=3
root.Properties.API.HTTP.AdminPath=/operator/basic.shtml
root.Properties.API.Metadata.Metadata=yes
root.Properties.API.Metadata.Version=1.0
root.Properties.API.RTSP.Version=2.01
root.Properties.API.RTSP.RTSPAuth=yes
root.Properties.API.WebService.WebService=yes
root.Properties.API.WebService.ONVIF.ONVIF=yes
root.Properties.API.WebService.ONVIF.Version=1.01
root.Properties.Audio.Audio=yes
root.Properties.Audio.Format=g711,g726,aac
root.Properties.Audio.DuplexMode=full,half,post,get
root.Properties.Audio.InputType=mic
root.Properties.Audio.Decoder.Format=g711,g726,axis-mulaw-128
root.Properties.Firmware.BuildNumber=1
root.Properties.Firmware.BuildDate=Jun 23 2016 13:16
root.Properties.Firmware.Version=5.20.4
root.Properties.HTTPS.HTTPS=yes
root.Properties.Image.Rotation=0,180
root.Properties.Image.Resolution=640x480,480x360,320x240,240x180,160x120,176x144
root.Properties.Image.Format=jpeg,mjpeg,mpeg4,h264,bitmap
root.Properties.Image.NbrOfViews=1
root.Properties.LightControl.LightControl=yes
root.Properties.Motion.Motion=yes
root.Properties.Motion.MaxNbrOfWindows=10
root.Properties.RemoteService.RemoteService=yes
root.Properties.RTC.RTC=no
root.Properties.System.Language=English
root.Properties.System.HardwareID=170
root.Properties.System.SerialNumber=00408C9A1F56
root.Properties.System.Architecture=armv5tejl
root.Properties.Tampering.Tampering=yes
root.Properties.TemperatureSensor.TemperatureSensor=yes
root.Properties.TemperatureSensor.TemperatureControl=no
root.Properties.TemperatureSensor.Fan=no
root.Properties.TemperatureSensor.Heater=no
root.RemoteService.Enabled=oneclick
root.RemoteService.Proxy=
root.RemoteService.RemoteServerURL=http://dispatcher.sts.axis.com:80/axissts/axissts.cgi,http://dispatcher.sts.axis.com:8080/axissts/axissts.cgi,http://dispatchse1.sts.axis.com:80/axissts/axissts.cgi,http://dispatchse1.sts.axis.com:8080/axissts/axissts.cgi,http://dispatchse2.sts.axis.com:80/axissts/axissts.cgi,http://dispatchse2.sts.axis.com:8080/axissts/axissts.cgi,http://dispatchus1.sts.axis.com:80/axissts/axissts.cgi,http://dispatchjp1.sts.axis.com:80/axissts/axissts.cgi,http://87.237.210.63:80/axissts/axissts.cgi,http://87.237.210.63:8080/axissts/axissts.cgi,http://85.235.16.249:80/axissts/axissts.cgi,http://85.235.16.249:8080/axissts/axissts.cgi,http://85.235.16.250:80/axissts/axissts.cgi,http://85.235.16.250:8080/axissts/axissts.cgi
root.RemoteService.Timeout=100
root.RemoteService.ConnectTimeout=15
root.RemoteService.BrandSpec=1275677567
root.RemoteService.Reconnect=0
root.RemoteService.TimeMode=NTP
root.SMTP.MailServer1=
root.SMTP.MailServer2=
root.SMTP.MailServerPort1=25
root.SMTP.MailServerPort2=25
root.SMTP.MailServerSSL1=no
root.SMTP.MailServerSSL2=no
root.SMTP.FromEmail=
root.SMTP.Authentication.A1.Enabled=no
root.SMTP.Authentication.A1.UserName=
root.SMTP.Authentication.A1.Password=
root.SMTP.Authentication.A1.WeakestMethod=Login
root.SMTP.Authentication.A1.UsePOP=no
root.SMTP.Authentication.A1.POPServer=
root.SMTP.Authentication.A2.Enabled=no
root.SMTP.Authentication.A2.UserName=
root.SMTP.Authentication.A2.Password=
root.SMTP.Authentication.A2.WeakestMethod=Login
root.SMTP.Authentication.A2.UsePOP=no
root.SMTP.Authentication.A2.POPServer=
root.SMTP.SSL.S1.RootCertificate=
root.SMTP.SSL.S1.ClientCertificate=
root.SMTP.SSL.S1.ClientPrivateKey=
root.SMTP.SSL.S1.ClientPrivateKeyPasswd=
root.SMTP.SSL.S2.RootCertificate=
root.SMTP.SSL.S2.ClientCertificate=
root.SMTP.SSL.S2.ClientPrivateKey=
root.SMTP.SSL.S2.ClientPrivateKeyPasswd=
root.SNMP.Enabled=no
root.SNMP.InitialUserPasswdSet=no
root.SNMP.InitialUserPasswd=*****
root.SNMP.EngineBoots=1
root.SNMP.V1=no
root.SNMP.V2c=no
root.SNMP.V3=no
root.SNMP.V1ReadCommunity=public
root.SNMP.V1WriteCommunity=write
root.SNMP.DSCP=0
root.SNMP.Trap.Enabled=no
root.SNMP.Trap.T0.Address=
root.SNMP.Trap.T0.Community=public
root.SNMP.Trap.T0.AuthFail.Enabled=no
root.SNMP.Trap.T0.ColdStart.Enabled=no
root.SNMP.Trap.T0.LinkUp.Enabled=no
root.SNMP.Trap.T0.WarmStart.Enabled=no
root.SOCKS.Enabled=no
root.SOCKS.Server=socks
root.SOCKS.ServerPort=1080
root.SOCKS.ServerType=4
root.SOCKS.LocalNetworks=10.0.0.0/255.0.0.0, 172.16.0.0/255.240.0.0, 192.168.0.0/255.255.255.0
root.SOCKS.UserName=
root.SOCKS.Password=
root.StatusLED.Usage=on
root.StatusLED.FlashInterval=4
root.StreamCache.Size=12582912
root.StreamCache.MaxGroups=20
root.StreamCache.S0.Enabled=no
root.StreamCache.S0.Options=
root.StreamCache.S0.RequestedLengthTime=30
root.StreamProfile.MaxGroups=20
root.StreamProfile.S0.Name=Quality
root.StreamProfile.S0.Description=Best image quality and full frame rate.
root.StreamProfile.S0.Parameters=videocodec=h264&resolution=640x480&compression=20&fps=0&videokeyframeinterval=8&videobitrate=0
root.StreamProfile.S0.Default.Name=Quality
root.StreamProfile.S0.Default.Description=Best image quality and full frame rate.
root.StreamProfile.S0.Default.Parameters=videocodec=h264&resolution=640x480&compression=20&fps=0&videokeyframeinterval=8&videobitrate=0
root.StreamProfile.S1.Name=Balanced
root.StreamProfile.S1.Description=Medium image quality and frame rate.
root.StreamProfile.S1.Parameters=videocodec=h264&resolution=640x480&compression=30&fps=15&videokeyframeinterval=15&videobitrate=0
root.StreamProfile.S1.Default.Name=Balanced
root.StreamProfile.S1.Default.Description=Medium image quality and frame rate.
root.StreamProfile.S1.Default.Parameters=videocodec=h264&resolution=640x480&compression=30&fps=15&videokeyframeinterval=15&videobitrate=0
root.StreamProfile.S2.Name=Bandwidth
root.StreamProfile.S2.Description=Low bandwidth with medium image quality.
root.StreamProfile.S2.Parameters=resolution=320x240&compression=50&fps=15&videokeyframeinterval=32&videobitrate=250&videobitratepriority=framerate&videocodec=h264
root.StreamProfile.S2.Default.Name=Bandwidth
root.StreamProfile.S2.Default.Description=Low bandwidth with medium image quality.
root.StreamProfile.S2.Default.Parameters=videocodec=h264&resolution=640x480&compression=50&fps=15&videokeyframeinterval=32&videobitrate=250&videomaxbitrate=1000&videobitratepriority=framerate
root.StreamProfile.S3.Name=Mobile
root.StreamProfile.S3.Description=Mobile device settings.
root.StreamProfile.S3.Parameters=videocodec=h264&resolution=176x144&compression=50&fps=15&videokeyframeinterval=32&videobitrate=120&videomaxbitrate=128&videobitratepriority=quality&audio=0
root.StreamProfile.S3.Default.Name=Mobile
root.StreamProfile.S3.Default.Description=Mobile device settings.
root.StreamProfile.S3.Default.Parameters=videocodec=h264&resolution=176x144&compression=50&fps=15&videokeyframeinterval=32&videobitrate=120&videomaxbitrate=128&videobitratepriority=quality&audio=0
root.System.BoaPort=80
root.System.AlternateBoaPort=0
root.System.BoaDSCP=0
root.System.BoaProtViewer=password
root.System.RootPwdSet=yes
root.System.BoaGroupPolicy.admin=http
root.System.BoaGroupPolicy.operator=http
root.System.BoaGroupPolicy.viewer=http
root.Tampering.T0.DarkDetectionEnabled=yes
root.Tampering.T0.MinDuration=20
root.Tampering.T0.DarkThreshold=90
root.Time.ServerTime=10:38:13
root.Time.ServerDate=2016-10-26
root.Time.ObtainFromDHCP=no
root.Time.POSIXTimeZone=CET-1CEST,M3.5.0,M10.5.0/3
root.Time.SyncSource=NTP
root.Time.DST.Enabled=yes
root.Time.NTP.VolatileServer=
root.Time.NTP.Server=192.168.69.251
root.WebService.Enabled=no
root.WebService.UsernameToken.ReplayAttackProtection=yes
root.WebService.UsernameToken.WaitForNTP=no
How yould you do this in C#?
You can use an ExpandoObject with a params indexer to access it, something like this (that's just an example, you'll need to do some testing around it and handle things like single key access etc):
public class AssociativeArray
{
private dynamic _data = new ExpandoObject();
public object this[params string[] keys]
{
get
{
dynamic lastObject = _data;
foreach (var key in keys.Take(keys.Count() - 1))
{
var dic = lastObject as IDictionary<string, Object>;
lastObject = dic[key] as ExpandoObject;
}
return (lastObject as IDictionary<string, object>)[keys.Last()];
}
set
{
dynamic lastObject = _data;
foreach (var key in keys.Take(keys.Count() - 1))
{
var dic = lastObject as IDictionary<string, Object>;
dic.Add(key, new ExpandoObject());
lastObject = dic[key];
}
(lastObject as IDictionary<string, object>).Add(keys.Last(), value);
}
}
}
you can then use it like this:
var arr = new AssociativeArray();
arr["root", "Audio", "Dup"] = 33;
Console.Write(arr["root", "Audio", "Dup"]);
PS. I'm using the suggestion from this answer to cast the ExpandoObject as a Dictionary
You can use a ExpandoObject for this case. You can cast the ExpandoObject to a Dictionary and add the values:
var text = File.ReadAllText("$settingsfile");
var keyValuePairs = text.Split('\n')
.Select(line => Tuple.Create(line.Split('=')[0].Trim(), line.Split('=')[1].Trim()));
dynamic root = new ExpandoObject();
foreach (var tup in keyValuePairs)
{
var path = tup.Item1.Split('.').Skip(1).ToList();
var setting = path.Last();
var value = tup.Item2;
IDictionary<string, object> resolvedObject = root;
foreach (var component in path.Take(path.Count - 1))
{
if (!resolvedObject.ContainsKey(component))
{
resolvedObject.Add(component, new ExpandoObject());
}
resolvedObject = (IDictionary<string, object>) resolvedObject[component];
}
resolvedObject[setting] = value;
}
Console.WriteLine(">> " + root.System.BoaPort);
Thanks to KMoussa!
Not the most beautiful code, but it works:
public class cAssociativeArray
{
private dynamic _data = new ExpandoObject();
public object this[params string[] keys]
{
get { return getValue(keys); }
set { setValue(value, keys); }
}
private void setValue(object _value, params string[] keys)
{
dynamic lastObject = _data;
string lKey = keys.Last();
foreach (var key in keys.Take(keys.Count() - 1))
{
var dic = lastObject as IDictionary<string, Object>;
if (!dic.ContainsKey(key))
{
dic.Add(key, new ExpandoObject());
}
lastObject = dic[key];
}
if ((lastObject as IDictionary<string, object>) != null)
{
if (!(lastObject as IDictionary<string, object>).ContainsKey(keys.Last())) // add value
{
(lastObject as IDictionary<string, object>).Add(lKey, _value);
}
else //change value
{
if (keys.Count() > 1)
{
(lastObject as IDictionary<string, object>)[lKey] = _value;
}
}
}
}
private string getValue(params string[] keys)
{
dynamic lastObject = _data;
string lKey = keys.Last();
foreach (var key in keys.Take(keys.Count() - 1))
{
var dic = lastObject as IDictionary<string, Object>;
if ((dic != null) && (dic.ContainsKey(key)))
{
lastObject = dic[key] as ExpandoObject;
}
else
{
return null;
}
}
if (((lastObject as IDictionary<string, object>) != null) && ((lastObject as IDictionary<string, object>).ContainsKey(lKey))
&& (!((lastObject as IDictionary<string, object>)[lKey] is ExpandoObject)))
{
return (lastObject as IDictionary<string, object>)[lKey].ToString();
}
else
{
return null;
}
}
public string[] getKeys(params string[] keys)
{
dynamic lastObject = _data;
foreach (var key in keys.Take(keys.Count()))
{
var dic = lastObject as IDictionary<string, Object>;
if ((dic != null) && (dic.ContainsKey(key)))
{
lastObject = dic[key] as ExpandoObject;
}
else
{
return null;
}
}
if ((lastObject as IDictionary<string, object>) != null)
{
return (lastObject as IDictionary<string, object>).Keys.ToArray();
}
else
{
return null;
}
}
public int countKeys(params string[] keys)
{
dynamic lastObject = _data;
foreach (var key in keys.Take(keys.Count()))
{
var dic = lastObject as IDictionary<string, Object>;
if ((dic != null) && (dic.ContainsKey(key)))
{
lastObject = dic[key] as ExpandoObject;
}
else
{
return -1;
}
}
if ((lastObject as IDictionary<string, object>) != null)
{
return (lastObject as IDictionary<string, object>).Count();
}
else
{
return -1;
}
}
public void printDebug(params string[] keys)
{
Debug.WriteLine("");
printDebugSub("", keys);
Debug.WriteLine("");
}
private void printDebugSub(string spaceStr, params string[] keys)
{
dynamic lastObject = _data;
foreach (var key in keys.Take(keys.Count()))
{
var dic = lastObject as IDictionary<string, Object>;
if ((dic != null) && (dic.ContainsKey(key)))
{
lastObject = dic[key] as ExpandoObject;
}
}
if ((lastObject as IDictionary<string, object>) != null)
{
foreach (string keyStr in (lastObject as IDictionary<string, object>).Keys.ToArray())
{
string valueStr = "";
if (!((lastObject as IDictionary<string, object>)[keyStr] is ExpandoObject))
{
valueStr = " = " + (lastObject as IDictionary<string, object>)[keyStr].ToString();
}
Debug.WriteLine(spaceStr + keyStr + valueStr);
List<string> lst = keys.ToList();
lst.Add(keyStr);
printDebugSub(spaceStr.Trim() + "---- ", lst.ToArray());
}
}
else
{
return;
}
}
}