Deserialise JSON to C# array, where index is in the property name - c#

Could anyone provide their approach for deserializing the following JSON
{
"i": 5
"b0": "ABC",
"b1": "DEF",
"b2": "GHI",
"s0": "SABC",
"s1": "SDEF",
"s2": "SGHI",
}
into a class in C# to provide the same structure as this
class Example {
public int Index {get;set;}
public string[] B {get;set;}
public string[] S {get;set;}
}
var b = new [] {"ABC", "DEF", "GHI"}
var s = new [] {"SABC", "SDEF", "SGHI"}
I generally use ServiceStack.Text, but Json.Net approach or even a BsonDocument from the MongoDb provider is fine.

Could use a JToken and use .Values() then .ToArray():
var json = "{\r\n \"b0\": \"ABC\",\r\n \"b1\": \"DEF\",\r\n \"b2\": \"GHI\",\r\n}";
var token = JToken.Parse(json);
var b = token.Values().ToArray();

This one solution might be useful too:
public class Example
{
public int Index { get; set; }
public string[] B { get; set; }
public string[] S { get; set; }
}
var strData = #"{'i': 5, 'b0': 'ABC','b1': 'DEF', 'b2': 'GHI', 's0': 'SABC', 's1': 'SDEF', 's2': 'SGHI',}";
var data = JsonConvert.DeserializeObject<JToken>(strData).Values().ToList();
Example result = new Example();
result.Index = data.Values().FirstOrDefault(x => x.Path == "i").Value<int>();
result.B = data.Values().Where(x => x.Path.StartsWith("b")).Select(x => x.Value<string>()).ToArray();
result.S = data.Values().Where(x => x.Path.StartsWith("s")).Select(x => x.Value<string>()).ToArray();
there is solution for dynamic name of arrays, but string array is not the best type in this case:
public class Example
{
public int Index { get; set; }
public Dictionary<string, string[]> Data { set; get; }
}
var s = #"{'i': 5, 'b0': 'ABC','b1': 'DEF', 'b2': 'GHI', 's0': 'SABC', 's1': 'SDEF', 's2': 'SGHI',}";
var data = JsonConvert.DeserializeObject<JToken>(s).Values().ToList();
Example result = new Example();
result.Index = data.Values().FirstOrDefault(x => x.Path == "i").Value<int>();
result.Data = new Dictionary<string, string[]>();
var stringData = data.Values().Where(x => x.Path != "i").ToList();
stringData.ForEach(x =>
{
var key = x.Path[0].ToString();
if (!result.Data.ContainsKey(key))
{
result.Data.Add(key, new string[0]);
}
var currentValue = result.Data[key].ToList();
currentValue.Add(x.Value<string>());
result.Data[key] = currentValue.ToArray();
});

Related

get parameter base on value of another property c# expression tree

i have two class :
class JoinedMapVoucher
{
public string Code1 { get; set; }
public string Code2 { get; set; }
public string Code3 { get; set; }
public DL DL1 { get; set; }
public DL DL2 { get; set; }
public DL DL3 { get; set; }
public DL DL4 { get; set; }
}
class DL
{
public long DLTypeRef { get; set; }
}
here i have a dictionary which keys are DLTypeRefs and values are code that should not be null for example in this example if DLTypeRef is 5 then Code1 property should have value and can not be null.
what i want to do is i get code that should not be null dynamically from dictionary.
and then i want to get that Code from JoinedMapVoucher type and check if it is null or not.
in code below i write comments where i want to get Code from dictionary and then get that property from JoinedMapVoucher parameter but it not work.
var dic = new Dictionary<long, string>();
dic.Add(5, "Code1");
dic.Add(-1, "NullCode");
var dicConst = Expression.Constant(dic);
var list = Expression.Constant(new List<long> { 1, 2, 3, 4, 5 });
var defaultDL = new DL { DLTypeRef = -1, Id = -1 };
var foos = new List<JoinedMapVoucher> {new JoinedMapVoucher { DL2 = new DL { DLTypeRef = 5, Id = 55 } } }.AsQueryable();
var containsMethod = typeof(List<long>).GetMethod(nameof(List<long>.Contains), new[] { typeof(long) });
var parameter = Expression.Parameter(typeof(JoinedMapVoucher), "JoinedMapVoucher");
for (var i = 1; i <= 4; i++)
{
var dl = Expression.PropertyOrField(parameter, "DL" + i.ToString());
var actualDL = Expression.Coalesce(dl, Expression.Constant(defaultDL));
var dlTypeRef = Expression.PropertyOrField(actualDL, "DLTypeRef");
var or1 = Expression.Or(Expression.Equal(dlTypeRef, Expression.Constant((long)-1)), Expression.Not(Expression.Call(list, containsMethod, dlTypeRef)));
var dicGetItemMethod = typeof(Dictionary<long, string>).GetMethod("get_Item", new[] { typeof(long) });
var getCode = Expression.Constant(Expression.Call(dicConst, dicGetItemMethod, dlTypeRef)); **//here this call should return code from dictionary which it can be Code5 or NullCode**
**var needCode=Expression.PropertyOrFeild(parameter,getCode) // then i want to get Code property from parameter dynamically**
var lambda = Expression.Lambda<Func<JoinedMapVoucher, bool>>(or1, new ParameterExpression[] { parameter });
Console.WriteLine(lambda.Body);
foos = foos.Where(lambda);
}
how to get property dynamically in needCode variable?
You need to build a switch statement which will switch on getCode value and return corresponding property. Something along this lines:
var getCode = Expression.Call(dicConst, dicGetItemMethod, dlTypeRef);
SwitchExpression switchExpr =
Expression.Switch(
getCode,
Expression.Constant("-1"), // default case when none is matched
new SwitchCase[]
{
Expression.SwitchCase( // switch case for "Code1" returned from dict
Expression.PropertyOrField(parameter, nameof(JoinedMapVoucher.Code1)),
Expression.Constant( nameof(JoinedMapVoucher.Code1))
),
Expression.SwitchCase( // switch case for "NullCode" returned from dict
Expression.PropertyOrField(parameter, nameof(JoinedMapVoucher.Code2)),
Expression.Constant("NullCode")
),
}
);
Which should represent something along this lines generated in your lambda:
var code = dictionary[dlTypeRef];
switch (code)
{
case "Code1":
return JoinedMapVoucher.Code1;
case NullCode:
return JoinedMapVoucher.Code2;
default:
return "-1";
}

Construct a dynamic object from another object that has dictionary<string, object> property with varying key-value pairs

I'm trying to collate the properties from an existing object that contains some static properties. The object also contains a Dictionary with key value pairs. These key value pairs should be dynamically added as properties to the new object I'm creating.
This is the set up for source object:
public class MyClass
{
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
public IDictionary<string, NameValuePair> PropD { get; set; }
}
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
This is where I create a list of the objects:
void Main()
{
var data = new List<MyClass>
{
new MyClass {PropA = "A1", PropB = "B1", PropC = "C1", PropD = new Dictionary<string, NameValuePair>
{
{ "Code11", new NameValuePair {Name = "PropD11", Value = "V11"}},
{ "Code12", new NameValuePair {Name = "PropD12", Value = "V12"}}
}},
new MyClass {PropA = "A2", PropB = "B2", PropC = "C2", PropD = new Dictionary<string, NameValuePair>
{
{ "Code21", new NameValuePair {Name = "PropD21", Value = "V21"}},
{ "Code22", new NameValuePair {Name = "PropD22", Value = "V22"}},
}},
new MyClass {PropA = "A3", PropB = "B3", PropC = "C3", PropD = new Dictionary<string, NameValuePair>
{
{ "Code12", new NameValuePair {Name = "PropD12", Value = "V31"}},
{ "Code21", new NameValuePair {Name = "PropD21", Value = "V32"}},
}},
};
//Extract column names from static properties
var staticColumns = typeof(MyClass).GetProperties().Where(n => n.PropertyType == typeof(string)).Select(p => p.Name);
//Extract column names from the dictionary
var dynamicColumns = data.Where(c => c.PropD.Any()).Select(c => c.PropD).SelectMany(c => c.Values.Select(v => v.Name)).Distinct().ToList();
//Combine to get a new list of columns
var columns = staticColumns.Union(dynamicColumns);
//Create object using columns
//Copy values from data to construct the new object.
}
I need help with logic to construct an object at runtime, that has the structure as below and the data correctly mapped.
PropA PropB PropC PropD11 PropD12 PropD21 PropD22
------------------------------------------------------------
A1 B1 C1 V11 V12
A2 B2 C2 V21 V22
A3 B3 C3 V31 V32
Assuming your possible property names are static, here is an answer class:
public class AnsClass {
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
public string PropD11 { get; set; }
public string PropD12 { get; set; }
public string PropD21 { get; set; }
public string PropD22 { get; set; }
}
And here is a LINQ statement to create a list of them:
var ans = data.Select(d => new AnsClass {
PropA = d.PropA,
PropB = d.PropB,
PropC = d.PropC,
PropD11 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD11")?.Value,
PropD12 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD12")?.Value,
PropD21 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD21")?.Value,
PropD22 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD22")?.Value,
})
.ToList();
If the members of PropD are dynamic, you can use the following to create ExpandoObjects but you are really better off just using MyClass the way it is - it has the best design for this already.
var ans2 = new List<dynamic>();
foreach (var d in data) {
dynamic eo = new ExpandoObject();
eo.PropA = d.PropA;
eo.PropB = d.PropB;
eo.PropC = d.PropC;
var deo = (IDictionary<string,object>)eo;
foreach (var dp in d.PropD.Values)
deo.Add(dp.Name, dp.Value);
ans2.Add(eo);
}

Converting list from one class to another

I'm trying to convert a group a complex list in C# (with Linq)
public class classA
{
public string Name { get; set; }
public int id { get; set; }
public string phone { get; set; }
public string interest { get; set; }
}
My first class is classA where it contains many list of elements like below.
List<classA> obj = new List<classA>();
obj.Add(new classA { id = 1, Name = "a", phone = "321", interest = "Playing" });
obj.Add(new classA { id = 1, Name = "2", phone = "123", interest="Tv" });
From this I need to group by using the id, So I've used Linq
var item = obj.GroupBy(a => a.id).Select(ac => ac.ToList()).ToList();
I've another class called classB which hold's the values others than id from the classA (where it'd be hold all subset of different attributes)
public class classB
{
public string Name { get; set; }
public string phone { get; set; }
public string interest { get; set; }
}
My Final Class looks likes,
public class Final
{
public int id { get; set; }
public List<classB> details { get; set; }
public Final()
{
details = new List<classB>();
}
}
My requirements are, after grouping the classA based on id, I need to convert that into my final class.
So I did like below,
public static void Main()
{
Console.WriteLine("Hello World");
List<classA> obj = new List<classA>();
obj.Add(new classA { id = 1, Name = "a", phone = "321", interest = "Playing" });
obj.Add(new classA { id = 1, Name = "b", phone = "123", interest = "Tv" });
obj.Add(new classA { id = 2, Name = "c", phone = "12322", interest = "Tv" });
obj.Add(new classA { id = 3, Name = "d", phone = "12333", interest = "Tv" });
var item = obj.GroupBy(a => a.id).Select(ac => ac.ToList()).ToList();
List<Final> finalobjList = new List<Final>();
foreach (var report in item)
{
Final finalObj = new Final();
foreach (var result in report)
{
finalObj.id = result.id;
}
var data = report.Select(x => new classB { Name = x.Name, phone = x.phone, interest = x.interest }).ToList();
finalObj.details = data;
finalobjList.Add(finalObj);
}
Console.WriteLine(finalobjList.Count());
}
I believe there is another easy way to achieve this using linq without using foreach multiple times
Appreciate your help!
You should be able to use your existing code except when you do your Select, select a new Final and use the group's Key for the Id, and convert the ac.ToList to a list of ClassB for the Details:
var item = obj
.GroupBy(a => a.id)
.Select(ac =>
new Final
{
Id = ac.Key,
Details = ac.Select(a =>
new classB {interest = a.interest, phone = a.phone, Name = a.Name})
.ToList()
});
var finalobjList = obj.GroupBy(a => a.id).Select(x => new Final() { id = x.Key, details = x.Select(y => new classB() { Name = y.Name }).ToList() } ).ToList();
(Code only answer - please dont hate me)
var items = (from a in obj
group new classB {Name = a.Name, phone = a.phone, interest = a.interest} by a.id into aa
select new Final { id = aa.Key, B= aa.ToList()}).ToList();

Custom File Parser

I am building a parser for a custom pipe delimited file format and I am finding my code to be very bulky, could someone suggest better methods of parsing this data?
The file's data is broken down by a line delimited by a pipe (|), each line starts with a record type, followed by an ID, followed by different number of columns after.
Ex:
CDI|11111|OTHERDATA|somemore|other
CEX001|123131|DATA|data
CCC|123131|DATA|data1|data2|data3|data4|data5|data6
. I am splitting by pipe, then grabbing the first two columns, and then using a switch checking the first line and calling a function that will parse the remaining into an object purpose built for that record type. I would really like a more elegant method.
public Dictionary<string, DataRecord> Parse()
{
var data = new Dictionary<string, DataRecord>();
var rawDataDict = new Dictionary<string, List<List<string>>>();
foreach (var line in File.ReadLines(_path))
{
var split = line.Split('|');
var Id = split[1];
if (!rawDataDict.ContainsKey(Id))
{
rawDataDict.Add(Id, new List<List<string>> {split.ToList()});
}
else
{
rawDataDict[Id].Add(split.ToList());
}
}
rawDataDict.ToList().ForEach(pair =>
{
var key = pair.Key.ToString();
var values = pair.Value;
foreach (var value in values)
{
var recordType = value[0];
switch (recordType)
{
case "CDI":
var cdiRecord = ParseCdi(value);
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key, CdiRecords = new List<CdiRecord>() { cdiRecord }
});
}
else
{
data[key].CdiRecords.Add(cdiRecord);
}
break;
case "CEX015":
var cexRecord = ParseCex(value);
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key,
CexRecords = new List<Cex015Record>() { cexRecord }
});
}
else
{
data[key].CexRecords.Add(cexRecord);
}
break;
case "CPH":
CphRecord cphRecord = ParseCph(value);
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key,
CphRecords = new List<CphRecord>() { cphRecord }
});
}
else
{
data[key].CphRecords.Add(cphRecord);
}
break;
}
}
});
return data;
}
Try out FileHelper, here is your exact example - http://www.filehelpers.net/example/QuickStart/ReadFileDelimited/
Given you're data of
CDI|11111|OTHERDATA|Datas
CEX001|123131|DATA
CCC|123131
You could create a class to model this to allow FileHelpers to parse the delimited file:
[DelimitedRecord("|")]
public class Record
{
public string Type { get; set; }
public string[] Fields { get; set; }
}
Then we could allow FileHelpers to parse in to this object type:
var engine = new FileHelperEngine<Record>();
var records = engine.ReadFile("Input.txt");
After we've got all the records loaded in to Record objects we can use a bit of linq to pull them in to their given types
var cdis = records.Where(x => x.Type == "CDI")
.Select(x => new Cdi(x.Fields[0], x.Fields[1], x.Fields[2])
.ToArray();
var cexs = records.Where(x => x.Type == "CEX001")
.Select(x => new Cex(x.Fields[0], x.Fields[1)
.ToArray();
var cccs = records.Where(x => x.Type == "CCC")
.Select(x => new Ccc(x.Fields[0])
.ToArray();
You could also simplify the above using something like AutoMapper - http://automapper.org/
Alternatively you could use ConditionalRecord attributes which will only parse certain lines if they match a given criteria. This will however be slower the more record types you have but you're code will be cleaner and FileHelpers will be doing most of the heavy lifting:
[DelimitedRecord("|")]
[ConditionalRecord(RecordCondition.IncludeIfMatchRegex, "^CDI")]
public class Cdi
{
public string Type { get; set; }
public int Number { get; set; }
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
}
[DelimitedRecord("|")]
[ConditionalRecord(RecordCondition.IncludeIfMatchRegex, "^CEX001")]
public class Cex001
{
public string Type { get; set; }
public int Number { get; set; }
public string Data1 { get; set; }
}
[DelimitedRecord("|")]
[ConditionalRecord(RecordCondition.IncludeIfMatchRegex, "^CCC")]
public class Ccc
{
public string Type { get; set; }
public int Number { get; set; }
}
var input =
#"CDI|11111|Data1|Data2|Data3
CEX001|123131|Data1
CCC|123131";
var CdiEngine = new FileHelperEngine<Cdi>();
var cdis = CdiEngine.ReadString(input);
var cexEngine = new FileHelperEngine<Cex001>();
var cexs = cexEngine.ReadString(input);
var cccEngine = new FileHelperEngine<Ccc>();
var cccs = cccEngine.ReadString(input);
Your first loop isn't really doing anything other than organizing your data differently. You should be able to eliminate it and use the data as it is from the file. Something like this should give you what you want:
foreach (var line in File.ReadLines(_path))
{
var split = line.Split('|');
var key = split[1];
var value = split;
var recordType = value[0];
switch (recordType)
{
case "CDI":
var cdiRecord = ParseCdi(value.ToList());
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key, CdiRecords = new List<CdiRecord>() { cdiRecord }
});
}
else
{
data[key].CdiRecords.Add(cdiRecord);
}
break;
case "CEX015":
var cexRecord = ParseCex(value.ToList());
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key,
CexRecords = new List<Cex015Record>() { cexRecord }
});
}
else
{
data[key].CexRecords.Add(cexRecord);
}
break;
case "CPH":
CphRecord cphRecord = ParseCph(value.ToList());
if (!data.ContainsKey(key))
{
data.Add(key, new DataRecord
{
Id = key,
CphRecords = new List<CphRecord>() { cphRecord }
});
}
else
{
data[key].CphRecords.Add(cphRecord);
}
break;
}
};
Caveat: This is just put together here and hasn't been properly checked for syntax.

Converting a list of options to a JavaScript arrary in C#

I need to get a JSON string that looks like the following:
{"1":[{"value":"1", "text":"Basketball"}, {"value":"2", "text":"Tennis"}, {"value":"3", "text":"Football"}],"3":[{"value":"4", "text":"futbol"}]}
The C# code responsible for building this looks like the following:
var sportsEntries = new Dictionary<byte, List<KeyValuePair<int, string>>>();
foreach (var department in Departments)
{
var deptOptions = SportList
.Where(x => x.DeptId == department.DeptId)
.ToDictionary(x => x.SportId, x => x.SportNameName).ToList();
sportsEntries .Add(department.DeptId, deptOptions);
}
var json = JsonConvert.SerializeObject(sportsEntries);
Unfortunately, this approach generates the wrong JSON. The JSON looks like this:
{"1":[{"Key":1,"Value":"Basketball"},{"Key":2,"Value":"Tennis"},{"Key":3,"Value":"Football"}],"3":[{"Key":4, "Value":"Futbol"}]}
I feel like I'm so close. Yet, I'm not sure how to update my C# code to make the resulting JSON look like the format I need. How do I update the C# to output the correct JSON?
Thank you!
You could use something like this:
var sportsEntries = new Dictionary<byte, List<object>();
foreach (var department in Departments)
{
var deptOptions = SportList
.Where(x => x.DeptId == department.DeptId)
.Select(x => new { value = x.SportId, text = x.SportNameName}).ToList();
sportsEntries .Add(department.DeptId, deptOptions);
}
var json = JsonConvert.SerializeObject(sportsEntries);
This solution replaces the initial KeyValuePair<int, string> with object and creates a list of anonymous objects, having the desired properties.
This works:
[TestFixture]
public class SoTest
{
[Test]
public void Test1()
{
var departments = new List<Department>
{
new Department
{
DeptId = 1
}
};
var sportList = new List<Sport>
{
new Sport
{
DeptId = 1,
SportId = 1,
SportName = "Basketball"
},
new Sport
{
DeptId = 1,
SportId = 2,
SportName = "Tennis"
}
};
var sportsEntries = new Dictionary<byte, List<Kvp>>();
foreach (var department in departments)
{
var deptOptions = sportList
.Where(x => x.DeptId == department.DeptId)
.Select(x => new Kvp { Value = x.SportId, Text = x.SportName }).ToList();
sportsEntries.Add(department.DeptId, deptOptions);
}
string json = JsonConvert.SerializeObject(sportsEntries);
Assert.IsNotNullOrEmpty(json);
Debug.Print(json);
}
}
public class Department
{
public byte DeptId { get; set; }
}
public class Sport
{
public byte DeptId { get; set; }
public int SportId { get; set; }
public string SportName { get; set; }
}
[DataContract]
public class Kvp
{
[DataMember(Name = "value")]
public int Value { get; set; }
[DataMember(Name = "text")]
public string Text { get; set; }
}

Categories