I have some data in the following JSON format that I need to parse:
{
"status":0,
"timestamp":"8:20pm",
"routes":[
{
"directions":[
"E Towne",
"ETP"
],
"routeID":"30"
},
{
"directions":[
"Johnson",
"Observatory"
],
"routeID":"81"
}
]
}
Using json.net, I have got want to get the following output:
30 E Towne – ETP
81 Johnson – Observatory
Using the code below, I get the following incorrect output:
30 E Towne – ETP
81 E Towne – ETP
How do I write out the directions array items to the corresponding routeID item? My code:
public class Route
{
public string routeID { get; set; }
public string directions { get; set; }
}
private void routeClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string jsonResults_routes = e.Result;
JObject routeData = JObject.Parse(jsonResults_routes);
JArray routeIdArray = (JArray)routeData["routes"];
JArray routeDirections = (JArray)routeData["routes"][0]["directions"];
List<Route> l = new List<Route>();
for (int i = 0; i < routeIdArray.Count; i++)
{
Route BusRoutes = new Route();
JObject routeIDarrayObject = (JObject)routeIdArray[i];
BusRoutes.routeID = (string)routeIDarrayObject["routeID"];
string sep = " - ";
string bothDirections = String.Join(sep, routeDirections);
List<string> sc = new List<string>();
string[] direction = new string[]{bothDirections};
sc.AddRange(direction);
foreach (string direx in sc)
{
BusRoutes.directions = direx;
}
l.Add(BusRoutes);
}
var newList = l.OrderBy(x => x.routeID).ToList();
lbRoutesData.ItemsSource = newList;
}
#competent_tech is correct in is analysis. If I can propose, I think it'll feel more natural if you work with actual objects. For example:
public class RouteInfo
{
public List<string> Directions { get; set; }
public string RouteID { get; set; }
}
public class RouteData
{
public int Status { get; set; }
public string Timestamp { get; set; }
public List<RouteInfo> Routes { get; set; }
}
And in your method:
var routeData = JsonConvert.DeserializeObject<RouteData>(e.Result);
return routeData.Routes
.Select(r => new Route
{
routeId = r.RouteID,
directions = String.Join(" - ", r.Directions)
})
.OrderBy(r => r.routeId)
.ToList();
Or manipulate your type object in other ways, more naturally.
Edit: For an easy way to generate classes based on a JSON string, go to json2csharp.
This is because your routeDirections is specifically asking for the first element in the array:
JArray routeDirections = (JArray)routeData["routes"][0]["directions"];
You need to move this logic inside the loop and use the current loop indexer:
for (int i = 0; i < routeIdArray.Count; i++)
{
Route BusRoutes = new Route();
JObject routeIDarrayObject = (JObject)routeIdArray[i];
BusRoutes.routeID = (string)routeIDarrayObject["routeID"];
JArray routeDirections = routeIDarrayObject["directions"];
Related
I need help converting an aggregation pipeline to map-reduce. I know that map-reduce is deprecated, however, I need it. I am using mongoDB version 4.4 and MongoDB.Driver version 2.11.0, so it still should be available. I got the following aggregation function
PipelineDefinition<Airplane, BsonDocument> pipeline = new BsonDocument[]
{
new BsonDocument("$unwind",
new BsonDocument("path", "$Tickets")),
new BsonDocument("$group",
new BsonDocument
{
{ "_id", "$Tickets.Price" },
{ "Count", new BsonDocument("$sum", 1) }
})
};
var results = await _airplanesCollection.Aggregate(pipeline).ToListAsync();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.AsBsonDocument[0].ToInt32(), result.AsBsonDocument[1].ToInt32());
}
return pricesCounts;
It works exactly as I want it, however when I tried several ways to create a map-reduce function, it either threw exceptions or returned no results
/*string map = #"
function() {
var ticket = this;
emit(ticket.Price, { count: 1 });
}";
string reduce = #"
function(key, values) {
var result = {count: 0};
values.forEach(function(value){
result.count += value.count;
});
return result;
}";*/
string map = #"
function() {
for(var i = 0; i < this.Tickets.lenght; i++){
emit( this.Tickets[i].Price, { count: 1} );
}
}";
string reduce = #"
function(key, values) {
reducedVal = { count: 0};
for (var idx = 0; idx < values.length; idx++) {
reducedVal.count += values[idx].count;
}
return reducedVal;
}";
var options = new MapReduceOptions<Airplane, KeyValuePair<int, int>>();
options.OutputOptions = MapReduceOutputOptions.Inline;
var results = _airplanesCollection.MapReduceAsync(map, reduce, options).Result.ToList();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.Key, result.Value);
}
return pricesCounts;
The commented-out code throws this exception
The non commented-out code returns empty list.
The result of aggregation pipeline and wanted output from map-reduce
This is how I give it to controller of swagger
[ProducesResponseType(typeof(List<string>), 200)]
[ProducesResponseType(typeof(BadRequestObjectResult), 400)]
[HttpGet("groupTickets")]
public async Task<List<string>> GroupTickets()
{
var groupedTickets = await _mongoService.GroupTickets();
var result = new List<string>();
foreach(var item in groupedTickets)
{
result.Add($"Price: {item.Key} Bought tickets: {item.Value}");
}
return result.Any() ? result : new List<string>() { $"No tickets were bought yet." };
}
This is how my Airplane and Ticket models look like
public class Airplane
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public DateTime? FlightTime { get; set; }
public string Destination { get; set; }
public IEnumerable<Ticket> Tickets { get; set; }
public Airplane()
{
Tickets = new List<Ticket>();
}
}
public class Ticket
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int TicketClass { get; set; }
public int Price { get; set; }
public string Destination { get; set; }
public string PassengerId { get; set; }
public Ticket() { Id = ObjectId.GenerateNewId().ToString(); }
}
Thank you for any help in advance.
I am trying to deserialize JSON file and want to assign to object ScanResult. var text showing all the values but scanresult showing null some null values. https://gyazo.com/ff2ce386f845394c458a88d43a1f30d8
please suggest if I am missing something.
//MY jSon File SCAN Test 1-1543045410222.json 's code
{
"at": 1543045410222,
"i": 1000,
"s": {
"Sensor1": ["OFF"],
"Sensor2": ["OFF"],
"DataReady1": ["OFF"],
"DataReady2": ["OFF"],
"CV1": [5.0],
"CV2": [6.0]
}
}
//ViewModel Code is as below:
public void ResendScanResult()
{
var ScanActivities = scanActivityManager.GetAll();
foreach (var item in ScanActivities)
{
var scanName = item.ScanName;
var dir = _dataFilePath + scanName + "\\";
var jsonFileName = string.Format("{0}{1}-{2}.json", dir, scanName, item.ScanDateEpoch);
string fileName = Path.GetFileName(jsonFileName);
// ScanResult scanResult = new ScanResult();
var text = File.ReadAllText(jsonFileName);
//var scanResults = JsonConvert.DeserializeObject<ScanResult>(text);
Common.Model.ScanResult scanResult = JsonConvert.DeserializeObject<Common.Model.ScanResult>(text);
var Mvm = MonitorViewModel.Instance;
// TargetProvider target = Mvm.GetTargetProvider(scanResult);
// Mvm.PublishToServer(target, scanResult);
}
}
and my scanRescult class code is as below :
namespace ABX.Common.Model
{
public class ScanResult
{
public ScanResult()
{
At = DateTimeOffset.Now.ToUnixTimeMilliseconds();
Interval = 1;
}
public string Name { get; set; }
public long At { get; set; }
public long Interval { get; set; }
public JObject Values { get; set; }
public string FileName { get; set; }
public JObject ToJson()
{
JObject json = new JObject
{
{ "at", At },
{ "i", Interval },
{ "s", Values }
};
return json;
}
Either rename your class properties to match your JSON, rename your JSON to match your class properties, or implement a custom JsonConverter, where you can implement arbitrary mapping.
Is there any library out there that can serialize objects with array properties to .csv?
Let's say I have this model:
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
...
public string[] AvailableVariants { get; set; }
}
Would something like that be possible to do?
Edit: I need to present some data in a csv/excel format. The thing is, I'm not sure if there is a simple way of achieving what I want with CSV serialization libraries or if I should rather focus on writing an Excel native file.
An example of result I'm looking for:
Product Name In Stock Price Variants
ABC 241 200 Normal
CAB 300 300 Normal
Red
Blue
CBA 125 100 Normal
White
Awesome
Red
ACB 606 75 Normal
Small
Large
X-Large
What would be the most efficient way to do this?
I'm not aware of any libraries that will do this, here's a console example of how I'd approach writing/reading from a CSV:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TestingProduct
{
class TestingProduct
{
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
public string[] AvailableVariants { get; set; }
public override string ToString() => $"{ProductName},{InStock},{Price}{(AvailableVariants?.Length > 0 ? "," + string.Join(",", AvailableVariants) : "")}";
public static Product Parse(string csvRow)
{
var fields = csvRow.Split(',');
return new Product
{
ProductName = fields[0],
InStock = Convert.ToInt32(fields[1]),
Price= Convert.ToDouble(fields[2]),
AvailableVariants = fields.Skip(3).ToArray()
};
}
}
static void Main()
{
var prod1 = new Product
{
ProductName = "test1",
InStock= 2,
Price = 3,
AvailableVariants = new string[]{ "variant1", "variant2" }
};
var filepath = #"C:\temp\test.csv";
File.WriteAllText(filepath, prod1.ToString());
var parsedRow = File.ReadAllText(filepath);
var parsedProduct = Product.Parse(parsedRow);
Console.WriteLine(parsedProduct);
var noVariants = new Product
{
ProductName = "noVariants",
InStock = 10,
Price = 10
};
var prod3 = new Product
{
ProductName = "test2",
InStock= 5,
Price = 5,
AvailableVariants = new string[] { "variant3", "variant4" }
};
var filepath2 = #"C:\temp\test2.csv";
var productList = new List<Product> { parsedProduct, prod3, noVariants };
File.WriteAllText(filepath2, string.Join("\r\n", productList.Select(x => x.ToString())));
var csvRows = File.ReadAllText(filepath2);
var newProductList = new List<Product>();
foreach (var csvRow in csvRows.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
newProductList.Add(Product.Parse(csvRow));
}
newProductList.ForEach(Console.WriteLine);
Console.ReadKey();
}
}
}
This code will work with a class that has a single object array property. Do you need something that can handle an object with multiple array properties?
I have written some kind of library to write csv files, have a look:
public static class CsvSerializer
{
public static bool Serialize<T>(string path, IList<T> data, string delimiter = ";")
{
var csvBuilder = new StringBuilder();
var dataType = typeof(T);
var properties = dataType.GetProperties()
.Where(prop => prop.GetCustomAttribute(typeof(CsvSerialize)) == null);
//write header
foreach (var property in properties)
{
csvBuilder.Append(property.Name);
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
//data
foreach (var dataElement in data)
{
foreach (var property in properties)
{
csvBuilder.Append(property.GetValue(dataElement));
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
}
File.WriteAllText(path, csvBuilder.ToString());
return true;
}
}
public class CsvSerialize : Attribute
{
}
Lets pretend you want to serialize following class:
public class MyDataClass
{
[CsvSerialize]
public string Item1 {get; set;}
[CsvSerialize]
public string Item2 {get; set;}
}
Then just do:
public void SerializeData(IList<MyDataClass> data)
{
CsvSerializer.Serialize("C:\\test.csv", data);
}
It takes a IList of your class and writes a csv.
It cant serialize arrays but that would be easy to implement.
In the top of the class
public class Country
{
public string country { get; set; }
}
And how i build the links
public void ImagesLinks()
{
try
{
int counter = 0;
int cnt = 0;
foreach (string countryCode in countriescodes)
{
imagesUrls.Add(countriesnames[counter]);
counter++;
cnt++;
for (; cnt < DatesAndTimes.Count(); cnt++)
{
string imageUrlIrTrue = firstUrlPart + countryCode + secondUrlPart + DatesAndTimes[cnt] + thirdUrlPart + "true";
string imageUrlIrFalse = firstUrlPart + countryCode + secondUrlPart + DatesAndTimes[cnt] + thirdUrlPart + "false";
imagesUrls.Add(imageUrlIrTrue);
imagesUrls.Add(imageUrlIrFalse);
if (cnt % 10 == 0)
break;
}
}
}
catch(Exception err)
{
string myerr = err.ToString();
}
}
What i have in the end is a List with names and the links of each name.
For example in the index 0 i have the name: Europe
Then then ext 18 indexs are links of Europe
Then the next name is in index 21: Alps
And then the next 18 indxs of Alps
What i want to do is using the class Country is when i will type for example:
Country. ( After the point i will have properties of all the names Europe ,Alps and so on so i can select one of the names ) Same like if i will type for example File. so after the point i will have properties like Create Copy and so on so when i will type Country i will have all the countries names Europe, Alps....
And then if i will make a loop over one of the names it will loop over it's 18 items. For example:
For (int i = 0; i < Country.Europe; i++)
{
// something to do with Country.Europe[i]
}
Or
For (int i = 0; i < Country.Alps; i++)
{
// something to do with Country.Alps[i]
}
So maybe each name/country should be a List of it self ?
For (int i = 0; i < Country.Europe.Count(); i++)
{
// something to do with Country.Europe[i]
}
But the idea is that i will be able easy to select a name from a properties list and when loop over the name it will loop over it's 18 items.
I threw this together real fast - it is how I would probably handle it. I added a little more, I think you just need a class to handle countries. In any case, you would need to adjust your loop to create this structure, so this isn't a perfect solution - but it's how I would do it. You could also use linq instead of loops, I did this more as a class-based answer than a "pretty" answer.
namespace classTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
World newWorld = new World();
Continent Europe = new Continent();
Europe.name = "Europe";
Country England = new Country();
England.name = "England";
List<string> imageUrl = new List<string>();
imageUrl.Add("url1-England");
imageUrl.Add("url2-England");
imageUrl.Add("url3-England");
England.imageUrls = imageUrl;
Europe.countries.Add(England);
newWorld.continents.Add(Europe);
Country France = new Country();
France.name = "France";
imageUrl = new List<string>();
imageUrl.Add("url1-France");
imageUrl.Add("url2-France");
imageUrl.Add("url3-France");
France.imageUrls = imageUrl;
Europe.countries.Add(France);
foreach (Continent continent in newWorld.continents)
{
Console.WriteLine(continent.name);
foreach (Country country in continent.countries)
{
Console.WriteLine(country.name);
foreach(string imageUri in country.imageUrls)
{
Console.WriteLine(imageUri);
}
}
}
}
}
public class World
{
public List<Continent> continents;
public World()
{
continents = new List<Continent>();
}
}
public class Continent
{
public string name;
public List<Country> countries { get; set; }
public Continent()
{
name = string.Empty;
countries = new List<Country>();
}
}
public class Country
{
public string name { get; set; }
public List<string> imageUrls { get; set; }
public Country()
{
name = string.Empty;
imageUrls = new List<string>();
}
}
}
In the following solution, I use an outer wrapper Country that provides static references to instances for the continents (e.g. Europe). The continents implement IEnumerable, so you can iterate over all countries of this continent or use LINQ to filter them.
public class CountryData : IEquatable<CountryData>{
public string Link { get; set; }
public bool Equals(CountryData other) {
if (other == null) {
return false;
}
return StringComparer.Ordinal.Equals(Link, other.Link);
}
public override int GetHashCode() {
return Link.GetHashCode();
}
}
public static class Country {
public static readonly Europe Europe = new Europe();
}
public class Europe : IEnumerable<CountryData> {
private List<CountryData> All => new List<CountryData> {
Austria,
Belgium
};
public CountryData Austria = new CountryData { Link = #"\Country\Austria" };
public CountryData Belgium = new CountryData { Link = #"\Country\Belgium" };
IEnumerator<CountryData> IEnumerable<CountryData>.GetEnumerator() {
return All.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return All.GetEnumerator();
}
}
Usage examples:
var austria = Country.Europe.Austria;
var belgium = Country.Europe.Single(c => c.Link.Contains("Belgium"));
foreach (var european in Country.Europe) {
Console.WriteLine(european.Link);
}
Edit
If you want to compare countries, CountryData must implement IEquatable<CountryData>
Usage example:
var isSame = Country.Europe.Austria == Country.Europe.Belgium;
// isSame is false
Pretty silly question to ask. but could not figure it out .
In a C# MVC Controller action , I need to model a Json Array for testing purposes.
But this shows me compilation errors instead of being a valid Json:
var result = {
"controllerId": "controller1",
"controllerName": "ControllerOne"
};
But this is perfectly valid :
var scheduleResult = new[]
{
new { scheduleId = "schedule1",scheduleName = "scheduleOne"},
new { scheduleId = "schedule2",scheduleName = "scheduleTwo"}
};
Why so ?
Also how to write a nested Json array :
I tried :
var scheduleResult = new[]
{
new { scheduleId = "schedule1",scheduleName = "scheduleOne",new[]{ new {doorId="Door1",doorName="DoorOne"}, new { doorId = "Door2", doorName = "DoorTwo" } } },
new { scheduleId = "schedule2",scheduleName = "scheduleTwo"}
};
But it shows errors in syntax. What to do ?
I Need to have nested array within each element of that array .
Thanks in advance.
Well, C# does not support the way you wrote. You can't just type in JSON in C# and expect it to work unfortunately. You can try like that with anonymous type:
var result = new
{
controllerId = "controller1",
controllerName = "ControllerOne",
myArray = new []
{
"a",
"b"
}
};
This is converted to JSON no problem if you return it as a result of API call.
The nested arrays you are talking about don't work because you need to give them a name, you can't have array property without a name. See example above.
Why don't you use Dictionary<TKey, TValue> with Newtonsoft.Json?
Simple json:
IDictionary<string, string> obj = new Dictionary<string, string>();
obj.Add("controllerId", "controller1");
obj.Add("controllerName", "ControllerOne");
// {"controllerId":"controller1","controllerName":"ControllerOne"}
string json = JsonConvert.SerializeObject(obj);
Nested json:
IList<string> obj = new List<string>();
IDictionary<string, string> first = new Dictionary<string, string>();
first.Add("scheduleId ", "schedule1");
first.Add("scheduleName", "scheduleOne");
IDictionary<string, string> second = new Dictionary<string, string>();
second.Add("scheduleId ", "schedule2");
second.Add("scheduleName", "scheduleTwo");
string first_json = JsonConvert.SerializeObject(first);
string second_json = JsonConvert.SerializeObject(second);
obj.Add(first_json);
obj.Add(second_json);
// ["{\"scheduleId \":\"schedule1\",\"scheduleName\":\"scheduleOne\"}","{\"scheduleId \":\"schedule2\",\"scheduleName\":\"scheduleTwo\"}"]
string json = JsonConvert.SerializeObject(obj);
Here first of all we should create model class with the same pattern of our return type
public class ScheduleModel
{
public List<Schedule> ScheduleList { get; set; }
}
public class Schedule
{
public int ScheduleId { get; set; }
public string ScheduleName { get; set; }
public List<Door> DoorList { get; set; }
}
public class Door
{
public int DoorId { get; set; }
public string DoorName { get; set; }
}
Now at the controller Action create the test data
List<Door> doorList = new List<Door>();
doorList.Add(new Door{DoorId = "Door1",DoorName = "DoorOne"});
doorList.Add(new Door{DoorId = "Door2",DoorName = "DoorTwo"});
List<Schedule> scheduleList = new List<Schedule>();
scheduleList.Add(new Schedule{
ScheduleId = "schedule1",
ScheduleName = "scheduleOne",
DoorList = doorList
});
scheduleList.Add(new Schedule
{
ScheduleId = "schedule2",
ScheduleName = "scheduleTwo",
});
return Json(scheduleList, JsonRequestBehavior.AllowGet);
If this answer benefits you please mark as an answer.