How to serialize specific Json object from C# - c#

I have a requirement where i need to serialize json object in below format
[{
"columns": [{
"title": "NAME"
}, {
"title": "COUNTY"
}],
"data": [
["John Doe", "Fresno"],
["Billy", "Fresno"],
["Tom", "Kern"],
["King Smith", "Kings"]
]
}]
Here i need to get this json object from two different source, one is Columns and other is data. Columns would come from a string which will be comma separated as
string columnNames = "Name, County";
and data would come from .net Datatable like
DataTable dt = new DataTable();
I tried with below code using JavaScriptSerializer but i am not able to format it in the required format. Actually, shared format is required to dynamically create jquery datatable. Here is my raw code in C#.
[WebMethod]
public static string ConvertDatadttoString(string appName)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("County", typeof(string));
dt.Rows.Add("vo", "go.com");
dt.Rows.Add("pa", "pa.com");
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
return serializer.Serialize(rows);
}
Above code is only serializing the DataTable and is not able to create in the required format. Thanks.

What I usually do, is I build a model based off of the data, and serialize that model.
This is how I'd imagine your model would look.
public class SampleClass
{
public IEnumerable<SampleItem> columns { get; set; }
public IEnumerable<IEnumerable<string>> data { get; set; }
}
public class SampleItem
{
public string title { get; set; }
}
And this is how I'd imagine you'd get the sample json
var sample = new List<SampleClass>
{
new SampleClass()
{
columns = new List<SampleItem>()
{
new SampleItem() {title = "NAME" },
new SampleItem() {title = "COUNTY" },
},
data = new List<List<string>>()
{
new List<string> { "John Doe", "Fresno" },
new List<string> { "Billy", "Fresno" },
new List<string> { "Tom", "Kern" },
new List<string> { "King Smith", "Kings" },
}
}
};
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(sample);
I'm sure you can figure out how to create that model based off of your real data. It's not that hard.

It's probably easier to create a class, but if you want to work with a Dictionary<string,object> then you need to first add an entry for your columns:
rows["columns"] = dt.Columns.Cast<DataTableColumn>()
.Select(c => new { title = c.ColumnName }).ToList();
And then you can add your data with something like:
rows["data"] = dt.Rows.Cast<DataRow>.Select(r => r.ItemArray).ToList();
Now you have a Dictionary<string,object> with two items columns and rows. columns contains a collection of objects with a property title and rows just contains an array of arrays for each row.
But this is a quick and dirty solution. I think creating a class as per #Sam I am's answer is cleaner and easier to maintain in the long run.
If you are starting with a comma separated list of column names, it really shouldn't be much harder to do. Something like:
var columns = columnNames.Split(","); // beware of column names that include commas!
row["columns"] = columns.Select(c => new { title = c });
row["data"] = dt.Rows.Cast<DataRow>.Select(r => columns.Select(c => r[c]).ToList());

Related

Create a tree node when serializing data

I have some data queried from a SQL database and I use this code to serialize them:
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
DataTable dt = new DataTable();
...
SqlDataAdapter adapt = new SqlDataAdapter();
adapt.Fill(dt);
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
return JsonSerializer.Serialize(rows);
It gave me this result when I serialize them:
{
"operator": "Unknown",
"extrainfo": "potential client",
"Name": "John Doe",
"ID": 568910,
"LastUpdate": "2021-07-22T00:00:00",
"Interested?": "Yes",
"Does it have a valid contract?": "No",
"Contract type": "Prepaid",
"Client willing to pay more?": "Yes, up to 20%",
"Comments": {}
}
I want all data that comes after lastUpdate column to be serialized inside another node, which is simply called interview.
Here is how I want to serialize them:
{
"operator": "Unknown",
"extrainfo": "potential client",
"Name": "John Doe",
"ID": 568910,
"LastUpdate": "2021-07-22T00:00:00",
"interview": [
{
"question" : "Interested?",
"answer": "Yes"
},
{
"question" : "Does it have a valid contract?",
"answer": "No"
},
{
"question" : "Contract type",
"answer": "Prepaid"
},
{
"question" : "Client willing to pay more?",
"answer": "Yes, up to 20%"
},
{
"question" : "Comments",
"answer": ""
}
]
}
Here it's how a database row looks like:
I want some help on how to do this.
All data that comes after lastUpdate column to be serialized inside another node
After is relative:
Your DataTable might define the columns in a different order then they should present in the json
Serializer might use different ordering then your database schema
Filtering
I would suggest an approach where you list those fields that should be serialized as properties and treat the rest of them as interview question-answer pairs.
var propertyFields = new[] { "operator", "extrainfo", "Name", "ID", "LastUpdate" };
Capturing data
In order to create the required output (for interview) you might need to introduce a class or a struct. I've introduced a named ValueTuple to avoid creating such. But depending on your runtime environment it may or may not available. UPDATE: ValueTuples are not supported by System.Text.Json.JsonSerializer
struct Interview
{
[JsonPropertyName("question")]
public string Question { get; set; }
[JsonPropertyName("answer")]
public string Answer { get; set; }
}
Wire up
Let's put all this things together
static readonly string[] propertyFields = new[] { "operator", "extrainfo", "Name", "ID", "LastUpdate" };
...
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
var interview = new List<Interview>();
foreach (DataColumn col in dt.Columns)
{
string name = col.ColumnName;
object value = dr[col];
if (propertyFields.Contains(col.ColumnName))
row.Add(name, value);
else
interview.Add(new Interview { Question = name, Answer = value.ToString() });
}
row.Add("interview", interview);
rows.Add(row);
}
#admiri Please look serialisation example in this link
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0
I substituted a list of tuples for the sql data. For the purposes of the algorithm it's going to be the same.
Note that the simplest way to do this, is to create a POCO class to hold the actual values with the nested "inteview" POCO. If this is coming from SQL then you should know the column structure.
Giving your question, I'm going to make the assumption that for whatever reason that isn't possible and you don't know the column structure ahead of time and you're doing this on the fly.
In that case you're best bet is to not use any POCO classes - including the dictionary you're currently using - and simply write out the data as JSON. One way to do that is as follows:
static List<(string name, string[] values)> Data = new()
{
("operator", new[] { "Unknown" } ),
("extrainfo", new[] { "potential client" }),
("Name", new[] { "John Doe" }),
("ID", new[] { "568910" }),
("LastUpdate", new[] { "2021-07-22T00:00:00" }),
("Interested?", new[] { "Yes" } ),
("Does it have a valid contract?", new[] { "No" } ),
("Contract type", new[] { "Prepaid" } ),
("Client willing to pay more?", new[] { "Yes, up to 20%" } ),
("Comments", new string[] { }),
};
static string Serilize(List<(string name, string[] values)> data)
{
using var output = new MemoryStream();
using (var writer = new Utf8JsonWriter(output, new JsonWriterOptions() { Indented = true }))
{
bool foundQA = false;
writer.WriteStartObject();
foreach (var row in data)
{
if (!foundQA)
{
foreach (var value in row.values)
{
writer.WritePropertyName(row.name);
if (null != value)
writer.WriteStringValue(value);
else
writer.WriteStringValue("");
}
if (row.name == "LastUpdate")
{
writer.WritePropertyName("interview");
writer.WriteStartArray();
foundQA = true;
}
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("question");
writer.WriteStringValue(row.name);
writer.WritePropertyName("answer");
writer.WriteStringValue(row.values.Length > 0 ? row.values[0] : "");
writer.WriteEndObject();
}
}
if (foundQA)
{
writer.WriteEndArray();
}
writer.WriteEndObject();
}
return Encoding.UTF8.GetString(output.ToArray());
}
static void Main(string[] args)
{
string formattedJson = Serilize(Data);
Console.WriteLine("Formatted output:");
Console.WriteLine(formattedJson);
}

List of maps in a DynamoDB data model

We have a data model with defined properties, but one of the properties allows for dynamic metadata (a list of maps or dictionaries). Using the document model, this property maps fine to a list of Document, however, when I'm having trouble getting this dynamic property to map to anything using DataModel. Is there a way to map dynamic data to documents inside a model class property?
Attempting to map it as a list of dictionaries (which matches the structure of the metadata) fails with the below error:
public List<Dictionary<string, object>> Events { get; set; }
Unable to convert [Amazon.DynamoDBv2.DocumentModel.Document] of type
Amazon.DynamoDBv2.DocumentModel.Document to
System.Collections.Generic.Dictionary`
Using a type of List<Document> got me the closest, which it now lists out 39 documents, but all the Documents have 0 keys, 0 values.
public List<Document> Events { get; set; }
Ex:
document["Events"].AsListOfDocument().First(); // works, contains the keys and values
datamodel.Events.First(); // does not work, it is an empty document
I know this is a quite old but hopefully this might help somebody else struggling with this.
Hi Devon,
In case you are trying to create the object to send it to your DynamoDB you can try mapping arbitrary data just as stated in AWS documentation for .NET
Here is the code from the documentation:
try
{
DynamoDBContext context = new DynamoDBContext(client);
// 1. Create a book.
DimensionType myBookDimensions = new DimensionType()
{
Length = 8M,
Height = 11M,
Thickness = 0.5M
};
Book myBook = new Book
{
Id = 501,
Title = "AWS SDK for .NET Object Persistence Model Handling Arbitrary Data",
ISBN = "999-9999999999",
BookAuthors = new List<string> { "Author 1", "Author 2" },
Dimensions = myBookDimensions
};
context.Save(myBook);
Once you have your data in the database you can append a new map to the list with something on the lines of:
var request = new UpdateItemRequest
{
TableName = "TableName",
Key = new Dictionary<string, AttributeValue>() { { "PartitionKey", new AttributeValue { S = "value" } } },
ExpressionAttributeNames = new Dictionary<string, string>()
{
{ "#A", "A" }
},
ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
{
{
":val", new AttributeValue
{
L = new List<AttributeValue>()
{
{
new AttributeValue
{
M = new Dictionary<string, AttributeValue>()
{
{ "Address", new AttributeValue{S = "Value" } },
{ "Latitude", new AttributeValue { S = position.Latitude.ToString() } },
{ "Longitude", new AttributeValue { S = position.Longitude.ToString() } }
}
}
}
}
}
}
},
UpdateExpression = "SET #A = list_append(#A, :val)"
};
try
{
var response = await client.UpdateItemAsync(request);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
This did the trick for me, I hope it does for someone else.
.
.
.
PS: BTW this is my first answer in Stackoverflow and it feels nice to try to contribute when I have came here multiple times for answers that saved me time jajajaja

Transform txt data to List separated by empty lines

I need some help trying to transform txt files in easy searchable data in c#.
My txt files are something like this:
Field1: Data
Field2: Data
UselessField1: Data
UselessField2: Data
UselessField3: Data
Field3: Data
Field3: Data
Field3: Data
Field4: Data
Field4: Data
Field4: Data
Field1: Data
Field2: Data
UselessField1: Data
UselessField2: Data
UselessField3: Data
Field3: Data
Field4: Data
Field4: Data
Field4: Data
Field4: Data
Fields3 and Field4 can have n lines and would be good to separate to other lists like Field1 with Fields3 and Field1 with Fields4 so I can link it later.
I also want to skip the Useless fields.
Maybe this is something simple but I'm complicating it too much, I would appreciate if someone could help. Thanks.
First i'd create a class with a meaningful name and meaningful properties:
public class MeaningfulClassName
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
//...
}
Now you can use LINQ to filter all for only the relevant lines in the file and split the key and the value by :. But if you want a safe and clean approach you need reflection and a dictionary to map the text-file fields with the properties in the class. For example:
var myClassType = typeof(MeaningfulClassName);
var allowedProperties = new Dictionary<string, PropertyInfo>
{
{ "Field1", myClassType.GetProperty("Property1") },
{ "Field2", myClassType.GetProperty("Property2") },
{ "Field3", myClassType.GetProperty("Property3") },
{ "Field4", myClassType.GetProperty("Property4") }
};
The LINQ query to select only the relevant tokens which also skips your useless fields:
var dataLines = File.ReadLines(path)
.Select(l => l.Split(':').Select(t => t.Trim()).ToArray())
.Where(arr => arr.Length == 2 && allowedProperties.ContainsKey(arr[0]));
Following loop reads the data and adds the instances of the class to a list:
var myList = new List<MeaningfulClassName>();
MeaningfulClassName currentObject = null;
foreach (string[] token in dataLines)
{
string fieldName = token[0];
string fieldValue = token[1];
PropertyInfo pi = allowedProperties[fieldName];
// first field specifies the beginning of the next object
if (fieldName == "Field1")
{
if (currentObject != null)
myList.Add(currentObject);
currentObject = new MeaningfulClassName();
}
pi.SetValue(currentObject, fieldValue);
}
if (currentObject != null)
myList.Add(currentObject);
now you have all objects and can search them easily, for example with LINQ
This code will turn your data into a list of dictionaries, and dictionaries will have field names as keys and list of data as value.
FileStream fs = new FileStream("data.txt", FileMode.OpenOrCreate);
StreamReader r = new StreamReader(fs);
List<Dictionary<string,List<string>>> alldata = new List<Dictionary<string,List<string>>>();
String[] lines = r.ReadToEnd().Split(new string[] { "\r\n" },StringSplitOptions.None);
alldata.Add(new Dictionary<string, List<string>>());
foreach (var item in lines)
{
if (item == "") { alldata.Add(new Dictionary<string, List<string>>()); continue; }
var lst = alldata[alldata.Count - 1];
string key = item.Split(':')[0];
if (key.StartsWith("Useless")) continue;
if (lst.ContainsKey(key))
{
lst[key].Add(item.Split(' ')[1]);
}
else {
lst[key] = new List<string>();
lst[key].Add(item.Split(' ')[1]);
}
}

Convert json respone into datatable

I have a json response from API and I need to store that information in a data table. Response is something like :
{
"columns": [
"firstName",
"lastName",
"email",
"password",
],
"rows": [
[
"Alpha",
"Tango",
"AlphaTango#domain.com",
"123"
],
[
"Charle",
"Tango",
"CharlieTango#domain.com",
"456"
]
]
}
I need it to be converted into the format :
firstName lastName email password
Alpha Tango AlphaTango#domain.com 123
Charle Tango CharlieTango#domain.com 456
I did the reverse with the help of below snippet, but am not able to convert json back into data table acceptable format :
JsonConvert.SerializeObject(new {
columns = dt1.Columns.Cast<DataColumn>().Select(x => x.ColumnName),
rows = dt1.AsEnumerable().Select(r => r.ItemArray),
});
Any suggestion or pointers will be highly appreciated
For this case you have to do some manual iterations over your json data. Can you try the code below?
static void Main(string[] args)
{
string json = "{\"columns\":[\"firstName\",\"lastName\",\"email\",\"password\",],\"rows\":[[\"Alpha\",\"Tango\",\"AlphaTango#domain.com\",\"123\"],[\"Charle\",\"Tango\",\"CharlieTango#domain.com\",\"456\"]]}";
dynamic jObject = JObject.Parse(json);
//Get columns of your object
List<string> columns = JsonConvert.DeserializeObject<List<string>>(jObject.columns.ToString());
//Get rows of your object
List<string[]> rows = JsonConvert.DeserializeObject<List<string[]>>(jObject.rows.ToString());
using (DataTable dt = new DataTable())
{
//Create columns
foreach (string column in columns)
dt.Columns.Add(new DataColumn(column));
//Add rows
foreach (string[] row in rows)
{
int columnOrdinal = 0;
DataRow newRow = dt.NewRow();
foreach (string value in row)
{
newRow.SetField<string>(columnOrdinal, value);
columnOrdinal++;
}
dt.Rows.Add(newRow);
}
}
}
Result for the code above:
Hope this helps

SQL Data to JSON C#

LIST Time Status
A 22:05 0
B 22:10 1
C 22:30 1
A 22:40 0
C 22:50 1
B 22:60 1
The above table needs to be converted to below JSON format
[
{ "name": "A",
data: [ [22:05,0],
[22:40,0]
]
},
{ "name": "B",
data: [ [22:10,1],
[22:60,1]
]
}
{ "name": "C",
data: [ [22:30,1],
[22:50,1]
]
}
]
The above is in a DataTable , now need to format the JSON,the below code does not give me in the same format.
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row = null;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
I would suggest reading the data from your table and creating a custom class to represent the data that needs to be serialized. You data would be represented in a class that looks like this:
public class MyType {
public string Name {get;set;}
public List<List<string>> Data {get;set;}
}
Then you would need a method to parse through your DataTable. Something like this might do it:
public List<MyType> ParseTable(DataTable dt) {
var myTypes = new List<MyType>();
var dictionary = new Dictionary<string, List<List<string>>>();
foreach(DataRow dr in dt.Rows) {
var name = dr[0];
var time = dr[1];
var status = dr[2];
if(!dictionary.ContainsKey(dr[0]) {
dictionary[name] = new List<List<string>>();
}
dictionary[name].Add(new List<string>{time, status});
}
foreach(var key = dictionary.Keys) {
myTypes.Add(new MyType {Name = key, Data = dictionary[key]});
}
return myTypes;
}
Then use a JSON serializer like http://james.newtonking.com/json to handle the actual serialization of your object into JSON.
That would look something like this:
public string Serialize(List<MyType> myTypes) {
return JsonConvert.SerializeObject(myTypes);
}
Please understand that this is freehand and off the cuff. So it may not be optimal and may need some tweaking. But this should get you where you are trying to go.

Categories