I have Json where i have 2 objects. I would like to know how to change value in my json, when other value is looking for. For example i would like to change "speciality" to "Warrior" for Person who's "Name" is Harry.
It's my Json
{
"Person": [
{
"Speciality": "Archer",
"Id": 432742,
"Name": "Charlie",
"Surname": "Evans",
"Items": [
"Bow",
"Arrow",
]
},
{
"Speciality": "Soldier",
"Id": 432534,
"Name": "Harry",
"Surname": "Thomas",
"Items": [
"Gun",
"Knife",
]
}
],
"Monster": [
{
"Name": "Papua",
"Skills": [
"Jump",
"SlowWalk",
]
},
{
"Name": "Geot",
"Skills": [
"Run",
"Push",
]
}
]
}
My classes
public class Person
{
public string Speciality { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<string> Items { get; set; }
}
public class Monster
{
public string Name { get; set; }
public List<string> Skills { get; set; }
}
public class Root
{
public List<Person> People { get; set; }
public List<Monster> Monsters { get; set; }
}
I tried something like this:
var result = JsonSerializer.Deserialize<Root>(jsonfile)
for (int i = 0; i < result.People.Count; ++i)
{
result.Where(w => w.Person.Name == "Harry").ToList().ForEach(s => s.Person.Speciality = "Warrior");
}
Thanks in advance for some help.
Your can use foreach loop
var result = JsonSerializer.Deserialize<Root>(jsonfile);
foreach (var person in result.Person)
{
if(person.Name == "Harry"){
person.Speciality = "";
}
}
Related
I've created two c# classes and then deserialize the Json returned to work with it natively.
class structureTree
{
public structureChildren[] children { get; set; }
}
class structureChildren
{
public structureChildren[] children { get; set; }
public string myentity { get; set; }
public bool sonGuide { get; set; }
public string from { get; set; }
public Int64 structureId { get; set; }
public string to { get; set; }
}
The Data returned is like this
[
{
"children": [
{
"children": [
{
"children": [
{
"children": [],
"myentity": "ENT2_A",
"from": "2019-10-01",
"structureId": 34353,
"to": null
},
{
"children": [
{
"children": [],
"myentity": "ENT3_A",
"from": "2019-10-01",
"structureId": 34349,
"to": null
},
{
"children": [],
"myentity": "ENT3_B",
"from": "2019-10-01",
"structureId": 34351,
"to": null
}
],
"myentity": "ENT2_B",
"from": "2019-10-01",
"structureId": 34348,
"to": null
}
],
"myentity": "ENT1_A",
"from": "2019-10-01",
"structureId": 34348,
"to": null
}
],
"myentity": "ENT0_A",
"from": "2019-10-01",
"structureId": 34348,
"to": null
}
]
}
]
I need to get all "myentity" elements and if it's possible in which level resides.
If not possible obtain level, another way is get all "myentity" for each level.
Probably there are much better and elegant ways of doing this. This is without thinking on it much:
void Main()
{
var st = JsonConvert.DeserializeObject<List<structureTree>>(myjson);
List<Tuple<string,int>> entities = new List<System.Tuple<string, int>>();
foreach (var stc in st)
{
foreach (var sc in stc.children)
{
GetMyEntity(entities, sc, 0);
}
}
foreach (var e in entities)
{
Console.WriteLine($"{e.Item1}, {e.Item2}");
}
}
void GetMyEntity(List<Tuple<string,int>> entities, structureChildren c, int level)
{
entities.Add(Tuple.Create(c.myentity,level));
level++;
foreach (var sc in c.children)
{
GetMyEntity(entities, sc, level);
}
}
class structureTree
{
public structureChildren[] children { get; set; }
}
class structureChildren
{
public structureChildren[] children { get; set; }
public string myentity { get; set; }
public bool sonGuide { get; set; }
public string from { get; set; }
public Int64 structureId { get; set; }
public string to { get; set; }
}
static readonly string myjson = #"[
{
""children"": [
{
""children"": [
{
""children"": [
{
""children"": [],
""myentity"": ""ENT3_A"",
""from"": ""2019-10-01"",
""structureId"": 34353,
""to"": null
},
{
""children"": [
{
""children"": [],
""myentity"": ""ENT3_B"",
""from"": ""2019-10-01"",
""structureId"": 34349,
""to"": null
},
{
""children"": [],
""myentity"": ""ENT3_C"",
""from"": ""2019-10-01"",
""structureId"": 34351,
""to"": null
}
],
""myentity"": ""ENT2_A"",
""from"": ""2019-10-01"",
""structureId"": 34348,
""to"": null
}
],
""myentity"": ""ENT1_1"",
""from"": ""2019-10-01"",
""structureId"": 34348,
""to"": null
}
]
}
]
}
]";
Output:
, 0
ENT1_1, 1
ENT3_A, 2
ENT2_A, 2
ENT3_B, 3
ENT3_C, 3
You can do it in two ways, with a stack/queue or recursion. I personally prefer stacks since recursion is hard(er) to debug. This is a quick draft. I'm sure someone else can give you a fancy recursion version with Linq, but I think this illustrates the steps you need to take better.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var structure = new StructureTree();
var currentStack = new Stack<StructureChildren>(structure.children);
var nextStack = new Stack<StructureChildren>();
var flatStructure = new List<KeyValuePair<int, string>>();
int currentLevel = 0;
bool loop = true;
while (loop)
{
var currentItem = currentStack.Pop();
foreach (var child in currentItem.children)
nextStack.Push(child);
flatStructure.Add(new KeyValuePair<int, string>(currentLevel, currentItem.myentity));
if (currentStack.Count == 0)
{
if (nextStack.Count == 0)
break;
currentLevel++;
currentStack = new Stack<StructureChildren>(nextStack);
nextStack.Clear();
}
}
}
public class StructureTree
{
public StructureChildren[] children { get; set; }
}
public class StructureChildren
{
public StructureChildren[] children { get; set; }
public string myentity { get; set; }
public bool sonGuide { get; set; }
public string from { get; set; }
public Int64 structureId { get; set; }
public string to { get; set; }
}
}
PS: Please look into the naming convention provided by Microsoft. Class names should be CapitalizedAtEveryWord and property names too. Your current naming scheme is typically used for Java, not C#.
I am trying to deserialize a json response I get from a web call. I have it 90 percent figured out. The only part I am having a hard time figuring out is there are these json arrays which have data in them and each array name is unique using the email address. I have not been able to figure out how to turn the Email Arrays into 1. Dynamic and having it create many lists or just a couple depending on what comes back in the response and also dynamically naming the list arrays to put the data into the Records class.
As you can see in the Records class I need this to be more dynamic and flexible to receive any and all emails.
Below is the json:
{
"result": {
"records": {
"joeblow#gmail.com": [
{
"OrderId": "d9535109-d305-4584-a503-8194bbcfcff2",
"CompletedOrderId": "BCFCFF2",
"CustomerId": 1212,
"CompletedTime": "2020-10-26 13:32:02",
"Email": "joeblow#gmail.com",
"ShippingFirstName": "Joe",
"ShippingMiddleName": "",
"ShippingLastName": "Blow",
"LineItems": {
"tfl.es.bluray": { "qty": 1 },
"booklets.en.ebook": { "qty": 1 }
}
}
],
"cleob#hotmail.com": [
{
"OrderId": "7bf97b3a-bc46-411c-bc30-12563326dba0",
"CompletedOrderId": "326DBA0",
"CustomerId": 1212,
"CompletedTime": "2020-10-26 20:07:44",
"Email": "cleob#hotmail.com",
"ShippingFirstName": "Cleo",
"ShippingMiddleName": "",
"ShippingLastName": "Blue",
"LineItems": {
"tfl.es.bluray": { "qty": 1 },
"booklets.en.ebook": { "qty": 1 },
"aos.en.pb": { "qty": 1 },
"course-tos.en.olr": { "qty": 1 },
"pow-hsk-nofilm.en.combo": { "qty": 1 },
"course-organizing.en.olr": { "qty": 1 }
}
}
],
"johnd#gmail.com": [
{
"OrderId": "630f0dda-94c3-4b82-a070-2554004dce29",
"CompletedOrderId": "04DCE29",
"CustomerId": 12345,
"CompletedTime": "2020-10-25 21:52:04",
"Email": "johnd#gmail.com",
"ShippingFirstName": "John",
"ShippingMiddleName": "",
"ShippingLastName": "Doe",
"LineItems": {
"tfl.es.bluray": { "qty": 1 },
"booklets.en.ebook": { "qty": 1 },
"aos.en.pb": { "qty": 1 },
"course-tos.en.olr": { "qty": 1 },
"pow-hsk-nofilm.en.combo": { "qty": 1 },
"course-organizing.en.olr": { "qty": 1 },
"oak-2007.en.cd": { "qty": 1 }
}
}
]
},
"errors": [
{
"id": "bademailaddress-yahoo.com",
"message": "Email address 'bademailaddress-yahoo.com' is not a valid email address"
}
]
},
"jsonrpc": "2.0",
"id": 12345634523
}
And the classes I made for the json deserialization:
public partial class JsonEmailDeSerializer
{
[JsonProperty("result")]
public Result Result { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
}
public partial class Result
{
[JsonProperty("records")]
public Records Records { get; set; }
[JsonProperty("errors")]
public List<Error> Errors { get; set; }
}
public partial class Error
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
public partial class Records
{
[JsonProperty("joeblow#gmail.com")]
public List<MailCom> JoeblowGmailCom { get; set; }
[JsonProperty("cleob#hotmail.com")]
public List<MailCom> CleobHotmailCom { get; set; }
[JsonProperty("johnd#gmail.com")]
public List<MailCom> JohndGmailCom { get; set; }
}
public partial class MailCom
{
[JsonProperty("OrderId")]
public Guid OrderId { get; set; }
[JsonProperty("CompletedOrderId")]
public string CompletedOrderId { get; set; }
[JsonProperty("CustomerId")]
public long CustomerId { get; set; }
[JsonProperty("CompletedTime")]
public DateTimeOffset CompletedTime { get; set; }
[JsonProperty("Email")]
public string Email { get; set; }
[JsonProperty("ShippingFirstName")]
public string ShippingFirstName { get; set; }
[JsonProperty("ShippingMiddleName")]
public string ShippingMiddleName { get; set; }
[JsonProperty("ShippingLastName")]
public string ShippingLastName { get; set; }
[JsonProperty("LineItems")]
public Dictionary<string, LineItem> LineItems { get; set; }
}
public partial class LineItem
{
[JsonProperty("qty")]
public long Qty { get; set; }
}
public partial class JsonEmailDeSerializer
{
public static JsonEmailDeSerializer FromJson(string json) => JsonConvert.DeserializeObject<JsonEmailDeSerializer>(json, FedExShipper.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this JsonEmailDeSerializer self) => JsonConvert.SerializeObject(self, FedExShipper.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
Common approach to deserializing json with dynamic property names is to use Dictionary<string, ...>, in this case - Dictionary<string, List<MailCom>> can be used for Records property:
public partial class Result
{
[JsonProperty("records")]
public Dictionary<string, List<MailCom>> Records { get; set; }
[JsonProperty("errors")]
public List<Error> Errors { get; set; }
}
Json.NET can treat json object properties as keys for dictionary which makes it suitable to deserialize such dynamic data. The same is true for System.Text.Json.
That will never serialize to a class properly.
You'll have to use a lower-level API like Utf8JsonReader to read that level in the document, at least.
I'm struggling to make this conversion happen, and not sure it's entirely feasible. My JSON from a third party could look like this:
{
"equipments": [
{
"serialNumber": "11-17-053",
"equipmentType_id": "589dda4952172110008870c7",
"created": 1508856453875,
"fieldOffice_id": "594af5425fbfca00111a0c20",
"clients_id": [],
"notes": "",
"isInService": true,
"metavalues": {
"0t78nzhp9w265avlvt": {
"item_ids": [
33121
]
},
"7ogz4kehqh8h3cwip8": {
"item_ids": [
33128
]
}
},
"schedules": [],
"id": "59ef52854d40a9009c787596"
},
{
"serialNumber": "11-17-054",
"equipmentType_id": "589dda4952172110008870c7",
"created": 1508856453875,
"fieldOffice_id": "594af5425fbfca00111a0c20",
"clients_id": [],
"notes": "",
"isInService": true,
"metavalues": {
"0t78nzhp9w265avlvt": {
"item_ids": [
33121
]
},
"7ogz4kehqh8h3cwip8": {
"item_ids": [
33128
]
}
},
"schedules": [],
"id": "59ef52854d40a9009c787597"
},
{
"serialNumber": "8-17-022",
"equipmentType_id": "589dda4952172110008870c7",
"created": 1505326964589,
"fieldOffice_id": "594af5425fbfca00111a0c20",
"clients_id": [],
"notes": "",
"isInService": true,
"metavalues": {
"0t78nzhp9w265avlvt": {
"item_ids": [
33121
]
},
"7ogz4kehqh8h3cwip8": {
"item_ids": [
33128
]
}
},
"schedules": [],
"id": "59b9777480e426009d01d48d"
},
{
"serialNumber": "22A-17-001",
"equipmentType_id": "589dda4952172110008870c7",
"created": 1504908025733,
"fieldOffice_id": "58b74b080c206710004ff726",
"clients_id": [
"59bbfdf5725cd00012fb15d8"
],
"notes": "",
"isInService": true,
"metavalues": {
"0t78nzhp9w265avlvt": {
"item_ids": [
33122
]
},
"7ogz4kehqh8h3cwip8": {
"item_ids": [
33128
]
},
"g99idmcqyuo2na9e6l": "YARD",
"709pm94prt2tpjt90y": 5,
"bgen1h4i5b6f8xa1kh": "9/8/2017 7:18:24 PM",
"yvtvsl8dedudqjtdud": "0.00000, 0.00000",
"aj3h2b5fdbic9s72m3": "Parex",
"8wt1re82xidjiv8rzi": "YARD"
},
"schedules": [],
"id": "59b312f93256d5009c4a73fb"
},....
This is obviously not a complete example, but should help get my question across.
Is there a way to create a C# class that pulls in certain fields only if they exist from the "metavalues" array?
My current class is as follows which works to get the data, but not exactly how I want.
public class Equipment
{
[JsonProperty("serialNumber")]
public string SerialNumber { get; set; }
public string equipmentType_id { get; set; }
public bool isInService { get; set; }
public List<string> clients_Id { get; set; }
public string fieldOffice_id { get; set; }
[JsonProperty("id")]
public string EquipmentId { get; set; }
[JsonProperty("metavalues")]
public Dictionary<string, object> metavalues { get; set; }
}
What I'm after, is taking the key of "g99idmcqyuo2na9e6l" which is optional in the metavalues, and store it in a property called "LeaseName".
I tried the following to no avail.
public class Equipment
{
[JsonProperty("serialNumber")]
public string SerialNumber { get; set; }
public string equipmentType_id { get; set; }
public bool isInService { get; set; }
public List<string> clients_Id { get; set; }
public string fieldOffice_id { get; set; }
[JsonProperty("id")]
public string EquipmentId { get; set; }
[JsonProperty("g99idmcqyuo2na9e6l")]
public string LeaseName { get; set; }
}
If I try to make a class for the metavalues section, I get an exception indicating that JSON.NET can't convert it to my object, hence why I used the Dictionary<string, object> option.
EDIT # 1: The accepted answer works for me, but for those who stumble upon this and truly need a property name from a nested array you could try the following.
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
if (metavalues != null)
{
if (metavalues.ContainsKey("g99idmcqyuo2na9e6l"))
{
string _LeaseName = (string)metavalues["g99idmcqyuo2na9e6l"];
LeaseName = _LeaseName;
}
}
}
I think my edit approach is a bit overkill but just throwing this out there.
Yes, this is possible, but since the lease value is still one level down in the JSON you need an intermediate class to hold it (to replace the dictionary).
public class Equipment
{
...
[JsonProperty("metavalues")]
public MetaValues MetaValues { get; set; }
}
public class MetaValues
{
[JsonProperty("g99idmcqyuo2na9e6l")]
public string LeaseName { get; set; }
}
Fiddle: https://dotnetfiddle.net/Ddqzc7
Okay first of all, the answer is probably very simple... But after 45 minutes of trying and googling I just can't figure it out!
So I have some problems getting this Json to parse correctly. I created the classes with http://json2csharp.com/ only it doesn't tell me the code to parse it.
My current classes:
public class Representations
{
public string thumb { get; set; }
public string large { get; set; }
public string full { get; set; }
}
public class Search
{
public string id { get; set; }
public string file_name { get; set; }
public Representations representations { get; set; }
}
public class SearchQuery
{
public List<Search> search { get; set; }
public int total { get; set; }
}
JSON:
{
"search": [
{
"id": "0300",
"file_name": "0300.JPG",
"representations": {
"thumb": "thumb.jpg",
"large": "large.jpg",
"full": "0300.jpg"
},
},
{
"id": "0000",
"file_name": "0000.JPG",
"representations": {
"thumb": "thumb.jpg",
"large": "large.jpg",
"full": "0000.jpg"
},
},
{
"id": "0d00",
"file_name": "0d00.JPG",
"representations": {
"thumb": "thumb.jpg",
"large": "large.jpg",
"full": "0d00.jpg"
},
}
],
"total": 3
}
and code:
searchresults = JsonConvert.DeserializeObject<List<SearchQuery>>(JSONCode);
You should deserialize to a SearchQuery, not List<SearchQuery>:
SearchQuery result = JsonConvert.DeserializeObject<SearchQuery>(JSONCode);
and then use the search property to access the list of search results:
List<Search> searchResults = result.search;
This is an example of the Json files that I want to work with:
{
"type": "FeatureCollection",
"totalFeatures": 213,
"features": [
{
"type": "Feature",
"id": "world_contries.1",
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
65.53080749511719,
37.248600006103516
],
[
65.6272964477539,
37.33319854736328
]
]
]
]
},
"geometry_name": "geom",
"properties": {
"name": "Afghanistan",
"iso_3_code": "AFG",
"iso_2_code": "AF",
"area": 65209,
"name_1": "Afghanistan",
"gmi_cntry": "AFG",
"region": "Asia",
"pop2005": 25067407,
"name_12": "Afghanistan"
}
},
{
"type": "Feature",
"id": "world_contries.2",
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
19.282489776611328,
42.18553924560547
],
[
19.397319793701172,
42.31707000732422
]
]
]
]
},
"geometry_name": "geom",
"properties": {
"name": "Albania",
"iso_3_code": "ALB",
"iso_2_code": "AL",
"area": 2740,
"name_1": "Albania",
"gmi_cntry": "ALB",
"region": "Europe",
"pop2005": 3153731,
"name_12": "Albania"
}
},
]
}
In this type of files I want to have the geometry type and coordinates of all features.
I'm currently using this method to access the file:
public static List<string> getCoords(string path)
{
//List<string> layers = new List<string>();
string url = path;
WebRequest request = WebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
try
{
WebResponse response = request.GetResponse();
StreamReader responseStream = new StreamReader(response.GetResponseStream());
string responseText = responseStream.ReadToEnd();
JObject o = JObject.Parse(responseText);
dynamic array = JsonConvert.DeserializeObject(responseText);
string type = array["features"].Children()["geometry"]["type"];
response.Close();
}
catch (WebException)
{
;
}
return null;
}
But it's not working. For instance:
array["features"].Children()["geometry"]["type"]
This is an Newtonsoft.JSon.Linq.JEnumerable
When I'm debugging in my Visual Studio, in the results view I can read "MultiPolygon", but I do I extract the value?
I used json2csharp (http://json2csharp.com) to generate some C# classes that match the JSON you provided..
public class Geometry
{
public string type { get; set; }
public List<List<List<List<double>>>> coordinates { get; set; }
}
public class Properties
{
public string name { get; set; }
public string iso_3_code { get; set; }
public string iso_2_code { get; set; }
public int area { get; set; }
public string name_1 { get; set; }
public string gmi_cntry { get; set; }
public string region { get; set; }
public int pop2005 { get; set; }
public string name_12 { get; set; }
}
public class Feature
{
public string type { get; set; }
public string id { get; set; }
public Geometry geometry { get; set; }
public string geometry_name { get; set; }
public Properties properties { get; set; }
}
public class RootObject
{
public string type { get; set; }
public int totalFeatures { get; set; }
public List<Feature> features { get; set; }
}
You could then modify your code so that it deserializes to the RootObject and it will be alot easier to work with instead of dynamic types...
var myObject = JsonConvert.DeserializeObject<RootObject>(responseText);
Then you could access it like this...
foreach (var feature in myObject.features)
{
var geometryType = feature.geometry.type;
....
}
You can simply loop through the JEnumerable to extract each geometry type :
.....
dynamic array = JsonConvert.DeserializeObject(json);
var types = array["features"].Children()["geometry"]["type"];
foreach (string type in types)
{
Console.WriteLine(type);
}