Serialize JSON with numbers as property names - c#

How can I serialize a DataTable which contains a list of phone numbers, BodyOverride and ChannelType to this structure? The final JSON should look like the sample below. I see some posts that suggest using a Dictionary, but not I'm not sure if I can achieve this.
{
"Addresses": {
"+1713XXXXXXX": {
"BodyOverride": "sent",
"ChannelType": "SMS"
},
"+1832XXXXXXX": {
"BodyOverride": "this is a text from PINPOINT",
"ChannelType": "SMS"
}
}
}

Try a using class structure like this:
public class Payload
{
public Dictionary<string, Item> Addresses { get; set; }
}
public class Item
{
public string BodyOverride { get; set; }
public string ChannelType { get; set; }
}
Assuming you are starting from a DataTable that looks like this:
DataTable dataTable = new DataTable();
dataTable.Columns.Add("Address");
dataTable.Columns.Add("BodyOverride");
dataTable.Columns.Add("ChannelType");
dataTable.Rows.Add("+1713XXXXXXX", "sent", "SMS");
dataTable.Rows.Add("+1832XXXXXXX", "this is a text from PINPOINT", "SMS");
...you can easily convert it to the desired class structure like this:
var payload = new Payload
{
Addresses = dataTable.Rows
.Cast<DataRow>()
.ToDictionary(row => (string)row["Address"],
row => new Item
{
BodyOverride = (string)row["BodyOverride"],
ChannelType = (string)row["ChannelType"]
})
};
...and finally serialize it to JSON using a decent serialization library like Json.Net:
string json = JsonConvert.SerializeObject(payload, Formatting.Indented);
Fiddle: https://dotnetfiddle.net/b7Ckzs
Important note: the above solution assumes that the phone numbers in the Address column will be distinct across all rows in the DataTable. If they are not, then this solution will not work, because dictionary keys are required to be unique. In that case you will need to split the data into multiple batches, or find some other solution to deal with duplicates.

Related

Convert Excel to JSON using LightweightExcelReader

I have a Excel file (.xlsx) with a sheet, which looks like this:
Name | Age | Country |
Nik 17 Switzerland
Thomas 28 Kuba
Waslim 12 Russia
I want to convert this excel sheet into JSON Format.
The result should look like this:
[
{
"Name":"Nik",
"Age":17,
"Country":"Switzerland"
},
{
"Name":"Thomas",
"Age":28,
"Country":"Kuba"
},
{
"Name":"Waslim",
"Age":12,
"Country":"Russia"
}
]
I would like to use the LightweightExcelReader framework. I know that there is a similar question already asked, but the answer uses OLEDB which shouldn't be best practice anymore. I would like to solve this transformation with a easier and faster framework.
IMPORTANT:
The number of rows and columns is dynamic and can vary, but the format of the excel sheet stays the from sheet to sheet the same.
Here is my attempt. As you can see i didn't manage alot and it's pretty basic. I managed to get the first row for the key in the JSON:
var excelReader = new ExcelReader(#"path\to\file\test.xlsx");
var sheetReader = excelReader[0];
IEnumerable<object> keys = sheetReader.Row(1);
How can I convert a Excel Sheet to JSON Format using the LightweightExcelReader Framework?
If you don't mind a dependency on Newtonsoft JSON you could do something like:
public static class ExcelJsonExtensionMethods
{
public static string ToJson(this SheetReader sheetReader)
{
IDictionary<int, string> HeaderNames = GetHeaderNames(sheetReader);
var jArray = new JArray();
while (sheetReader.ReadNext())
{
var jObject = new JObject();
do
{
var propertyName = HeaderNames[new CellRef(sheetReader.Address).ColumnNumber];
jObject[propertyName] = sheetReader.Value?.ToString();
} while (sheetReader.ReadNextInRow());
jArray.Add(jObject);
}
return jArray.ToString();
}
private static IDictionary<int, string> GetHeaderNames(SheetReader sheetReader)
{
var headerNames = new Dictionary<int, string>();
while (sheetReader.ReadNextInRow())
{
headerNames.Add(new CellRef(sheetReader.Address).ColumnNumber, sheetReader.Value?.ToString());
}
return headerNames;
}
}
Use it like:
var excelReader = new ExcelReader(#"path\to\file\test.xlsx");
var sheetReader = excelReader[0];
var sheetJson = sheetReader.ToJson();
Bear in mind that for this code to work:
The headers need to be on the first row
The header names need to be valid JSON property names
You can't have duplicate header names
There can't be any blank columns in the header
LightWeightExcelReader states on their github readme
If you want to map a spreadsheet to a collection of objects, use our sister project, ExcelToEnumerable
The sample on their sister project looks quite like what you are trying to achieve.
https://github.com/ChrisHodges/ExcelToEnumerable
public class Animal
{
public string Name { get; set; }
public string Colour { get; set; }
public int Legs { get; set; }
public decimal TopSpeed { get; set; }
}
IEnumerable<Animal> animals = "path/to/ExcelFile.xlsx".ExcelToEnumerable<Animal>();
and animals should then easily be serializable with Newtonsoft Json
using (StreamWriter file = File.CreateText(#"c:\animals.json"))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(file, animals);
}

Filter c# object with JObject

Let me preface by saying I'm very new to C# development so if the solution seems obvious I apologize.
I'm getting a JSON string back from a user and I need to filter a list of C# objects based on what the JSON string contains. The JSON can only have fields that my C# model has but I don't know what fields the JSON string will contain. My C# model looks something like this:
public class Enrollment {
public int Year { get; set; }
public int NumEnrolls { get; set; }
public int DaysIntoEnrollment { get; set; }
public string Name { get; set; }
}
The JSON will have one or more of these properties with values to filter out. It could look like this:
{
"Year": ["2020", "2019"],
"Name": ["CourseA", "CourseB"],
"DaysIntoEnrollment": "20"
}
I need to filter my list of Enrollment objects based on the above JSON. So I would want the end result to have all Enrollment objects that don't contain a Year of 2020 or 2019 for example.
I've gotten a filter to work with linq on a single property but my real model has much more properties that can be filtered and I'm looking for a compact solution that will work regardless of which properties are included in the JSON. This is what I have working
public void GetFilteredData(string filters) {
var enrollList = new List<Enrollments>(); // Pretend this contains a list of valid Enrollment data
var json = JObject.Parse(filters); // filters string is in the json format from above
var propsToFilter =
from p in json["Year"]
select p;
var filtered = enrollList.Where(e => !propsToFilter.Contains(e.Year.ToString())));
}
Is there a simple way to do this without manually going through each property like I did above?

Parse json file without key values

I'm trying to parse a json file with Json.net. The content of the json file is:
[
[ "240521000", "37.46272", "25.32613", "0", "71", "90", "15", "2016-07-18T21:09:00" ],
[ "237485000", "37.50118", "25.23968", "177", "211", "273", "8", "2015-09-18T21:08:00" ]
]
I created the following code:
WebClient wc = new WebClient();
string json = wc.DownloadString("data.json");
dynamic myObject = JsonConvert.DeserializeObject<dynamic>(json);
foreach (string item in myObject[0])
{
var x = item[0];
}
How can I loop through all the individual items without having a key?
You probably just need two nested foreach statements. Try something like this:
foreach (var items in myObject)
{
foreach (var item in items)
{
// do something
}
}
While diiN_'s answer answers the question, I don't think it's a good solution. Having had a look at the Marine Traffic API, it feels like they've made a poor JSON implementation, as the XML representation clearly has attribute names for the values. Their JSON should've been:
{"positions": ["position": {"mmsi": "311029000", "lat": "37.48617", "long": "24.37233", ...}]}
Because it isn't, we have a JSON string instead where it's a nested array, where the array is a two-dimensional array, and you'd have to hope the data model isn't changed to remove something, as you'd have to use an index to retrieve data.
However, if you look at the XML available from the API, the attributes have names. I would suggest that you instead download the XML, and parse this into an object - a model, in ASP.NET - which is strongly typed, and can be used more easily in a View.
Here's an example that I've got running. It uses XML parsing to first read the XML from the API, and then parse it to JSON, and finally an actual object.
First, the model class (Position.cs)
public sealed class Position
{
[JsonProperty("#MMSI")]
public string MMSI { get; set; }
[JsonProperty("#LAT")]
public string Latitude { get; set; }
[JsonProperty("#LON")]
public string Longitude { get; set; }
[JsonProperty("#SPEED")]
public string Speed { get; set; }
[JsonProperty("#HEADING")]
public string Heading { get; set; }
[JsonProperty("#COURSE")]
public string Course { get; set; }
[JsonProperty("#STATUS")]
public string Status { get; set; }
[JsonProperty("#TIMESTAMP")]
public string TimeStamp { get; set; }
}
Next, the parsing logic:
var client = new WebClient();
var xml = client.DownloadString("data.xml");
var doc = new XmlDocument();
doc.LoadXml(xml);
var json = JsonConvert.SerializeXmlNode(doc);
var positions = JObject.Parse(json).SelectToken("pos").SelectToken("row").ToObject<List<Position>>();
At the end of the parsing logic, you now have a list of Positions which you can pass to your view, and have it be strongly typed.
As a brief example:
// after you have the positions list
return View(positions);
Positions.cshtml
#model List<Positions>
<h2>Positions</h2>
#foreach (var position in Model)
{
<p>#position.MMSI (#position.Latitude, #position.Longitude)</p>
}
I hope this is useful to you. If you have any questions, drop me a comment.

Alter Json or ExtensionDataObject

I have a Json service I cannot alter as it is not mine.
Their Json is a formatted in a way that parsing it is difficult. It looks something like this.
"people": {
"Joe Bob": {
"name": "Joe Bob",
"id": "12345"
},
"Bob Smith": {
"name": "Bob Smith",
"id": "54321"
}
},
I would really prefer this was laid out like a JSon array, however it presently is not.
I am wondering the best approach here. Should I alter the Json to look like an array before I parse it or load up the ExtensionData and parse it from that?
There are other items in the feed that I do not have issue with. Just stuck with this one section.
Thanks
You can use json.net to deserialize the data (the json you pasted, and doing only one parsing, without modifying anything).
using dynamic foo = JsonConvert.DeserializeObject<dynamic>(data)
than, you can iterate the list using foo.people, accessing the Name and Value.
you can create a class (if you know what the schema is, and to deserialize the data into a list of the given class such as:
public class People
{
[JsonProperty(PropertyName="people")]
public IDictionary<string, Person> Persons { get; set; }
}
public class Person
{
[JsonProperty(PropertyName="name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
}
and than call:
var obj = JsonConvert.DeserializeObject<People>(data);
foreach (var item in obj.Persons.Values)
{
//item is instance of Person
}
Another good and possible option will be:
How can I navigate any JSON tree in c#?

How can I turn an anonymous type into a key value pair object

I'm trying to figure out a clean way of getting data back from my web service that is key/value pair.
Is there a way to take a query like this:
var q = db.Ratings.Select(x => new
{
x.pro,
x.con,
x.otherThoughts,
x.Item.title,
x.score,
x.subject
});
And just return a key value pair where the key would be the columns listed above, with their corresponding value?
Rather than creating a separate class?
myDict.Add("pro", q.pro);
myDict.Add("con", q.con);
This is not efficient and creating a class like this isn't either, especially if I have dozens of methods:
public class Rating
{
public string pro { get; set; }
public string con { get; set; }
}
All of my searches turn up examples that contain the previous two code samples.
I don't recommend to use the 'untyped' approach that you are looking at.
I would use typed objects instead, the ones that you don't want to create.
However, here is the answer to your question. You can use DataTable object for what you need. Like this:
var items = new[]
{
new Item { Id = 1, Name = "test1" },
new Item { Id = 2, Name = "test2" }
};
var dataTable = new DataTable();
var propeties = typeof(Item).GetProperties();
Array.ForEach(propeties, arg => dataTable.Columns.Add(arg.Name, arg.PropertyType));
Array.ForEach(items, item => dataTable.Rows.Add(propeties.Select(arg => arg.GetValue(item, null)).ToArray()));
return dataTable;

Categories