Cross-referencing two parts of one JSON file in C#? - c#

Newbie question. I have a semi-complex JSON file that I have parsed in my program. The relevant parts are below:
{
"version": "36",
"released": "20220223",
"samples": {
"Samp1": [
{
"code": "A01",
"status": "Optimal",
"bestBy": "20210918",
"expires": "20211018",
"elementKeys": {
"H": [
"Hydrogen-std1-slt4"
]
}
},
{
"code": "A02",
"status": "Optimal",
"bestBy": "20211201",
"expires": "20220501",
"elementKeys": {
"H": [
"Hydrogen-std1-slt5"
]
}
},
{
"code": "A03",
"status": "Optimal",
"bestBy": "20230201",
"expires": "20230801",
"elementKeys": {
"H": [
"Hydrogen-std1-slt6"
]
}
}
],
"Samp2": [ ...
"Samp3": [ ...
"Samp4": [ ...
},
"element": {
"Hydrogen-std1-slt4": {
"format": "1.0",
"position": 4,
"standard": "std1",
...
...
}
What I need to do is populate some windows form controls with data from this file. However, thanks to the odd architecture, I'm a little frustrated over how to populate the controls for "code", "status", "bestBy", etc contained within the "samples" arrays (Samp1, 2, 3, and 4).
Only some of the samples are relevant and the relevancy is defined by whether or not the name of the element key is found further below in the JSON file. So, using the example above, within "element", the object "Hydrogen-std1-slt4" is found in the body of the JSON file with its own key-value pairs. I would like the program to see that and recognize that "Hydrogen-std1-slt4" is also found within the object in the "Samp1" array with the code "A01", the status of "Optimal", the best-by date of "20210918", and that it expires on "20211018". Since it isn't found in any other place in "Samp1", the program can ignore the other objects.
What would be the easiest, most logical way to go about making a conditional for that?

try this code
var jsonParsed = JObject.Parse(json);
string[] elementKeys = ((JObject)jsonParsed["element"]).Properties().Select(x => x.Name).ToArray();
List<Samp> data= GetData(((JObject) jsonParsed["samples"]), elementKeys);
public List<Samp> GetData(JObject samples, string[] elementKeys)
{
List<Samp> result = new List<Samp>();
foreach (var element in samples.Properties())
foreach (var item in element.Value)
if ( item["elementKeys"]["H"]!=null
&& item["elementKeys"]["H"].ToObject<string[]>()
.Any(x => elementKeys.Contains(x)) )
result.Add(item.ToObject<Samp>());
return result;
}
classes
public class Samp
{
public string code { get; set; }
public string status { get; set; }
public string bestBy { get; set; }
public string expires { get; set; }
public ElementKeys elementKeys { get; set; }
}
public class ElementKeys
{
public List<string> H { get; set; }
}

Related

Create object of json when elements are variables

i need help for deserialize this json on C#.
{
"date": "2021-01-04T16:58:06-03:00",
"resultsInThisPage": 2,
"currentPage": 1,
"totalPages": 1,
"paymentOrders": {
"3AC525B8EF5F413D86CFDC9318E8B3F8": {
"code": "3AC525B8EF5F413D86CFDC9318E8B3F8",
"status": 5,
"amount": 3.00,
"grossAmount": 3,
"lastEventDate": "2021-01-04T16:54:36-03:00",
"schedulingDate": "2021-01-04T16:54:31-03:00",
"transactions": [
{
"code": "AF63C1B13EF14F318C51119CBE1E5CA4",
"date": "2021-01-04T16:54:32-03:00",
"status": 3
}
],
"discount": {
"type": "DISCOUNT_PERCENT",
"value": 0
}
},
"1E5A8F8D35D34938A12281BD45C62DBE": {
"code": "1E5A8F8D35D34938A12281BD45C62DBE",
"status": 1,
"amount": 3.00,
"grossAmount": 0,
"lastEventDate": "2021-01-04T16:54:33-03:00",
"schedulingDate": "2021-02-04T05:54:31-03:00",
"transactions": [],
"discount": {
"type": "DISCOUNT_PERCENT",
"value": 0
}
}
}
}
I´m trying use one Class for make this, but the elements of "paymentOrders" are variables.
How is the best way for do this?
I need create a object to manipulate these values.
paymentOrders would be a Dictionary<string,SomeObject> where SomeObject represents the object with all the properties like code, status etc
You can use Dictionary<string, SomeType> (or even Dictionary<Guid, SomeType> in your case, based on provided sample) to represent dynamic property names for paymentOrders json object. Something like this should do the trick:
public class Root {
// your dynamic prop
public Dictionary<string, PaymentOrder> paymentOrders { get; set; }
public DateTime date { get; set; }
// ... the rest o properties
}
public class PaymentOrder {
public string code { get; set; }
public int status { get; set; }
// ... the rest o properties
}

Compare two Jsons

For example, I have two Jsons: the first one I gave from Test Constructor, the second I gave such a user result of a test (I'm using the JS libriry - survey.js) :
The First :
{
"pages": [
{
"name": "page 1",
"elements": [
{
"type": "checkbox",
"name": "question 1",
"correctAnswer": [
"item1",
"item3"
],
"choices": [
"item1",
"item2",
"item3"
]
},
{
"type": "radiogroup",
"name": "question 2",
"correctAnswer": "item2",
"choices": [
"item1",
"item2",
"item3"
]
}
]
}
]
}
The Second:
{
"question 1":["item3","item1"],
"question 2":"item2"
}
How should I compare those two Jsons by correctAnswer?
I require result such:
question 1 - wrong,
question 2 - right.
as user gsharp mentioned you can use json.NET to achieve this.
Just do the following: create your classes that will hold the survey form:
public class SurveyForm
{
[JsonProperty("pages")]
public IEnumerable<SurveyPage> Pages { get; set; }
}
public class SurveyPage
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("elements")]
public IEnumerable<SurveyPageElement> Elements { get; set; }
}
public class SurveyPageElement
{
// I think you can do the rest
}
public class SurveyResult
{
// same as the other classes
}
Do you notice the JsonProperty Attribute? Use that to tell json.NET where to find the related properties in your json. You actually don't need to do that because json.NET does some magic to find the correct properties by it's own but I think it is useful to do it by my own.
Then deserialize the two Jsons:
var surveyForm = JsonConvert.DeserializeObject<SurveyForm>(surveyFormJson);
var surveyResult = JsonConvert.DeserializeObject<Dictionary<string,IEnumerable<string>>>(surveyResultJson);
EDIT: To compare the both do something like this:
foreach(var page in surveyForm.Pages)
{
foreach(var element in page.Elements)
{
if(element.CorrectAnswers.All(surveyResult[element.Name].Contains))
{
// right
}
else
{
// wrong
}
}
}

Serialize some relationships to JSON, but not all

I have the following problem: I am trying to serialize an object (object1) to Json using Newtonsoft.Json package. I need to be able to send it to a server. The problem is object1 has several referenced objects, some that should be created together with object1, but one of them is "read only" on the server, so it must be send as a relationship.
I am using string json = JsonConvert.SerializeObject
Example:
<code>
[DataContract]
public class Object1
{
// Simple Properties
[JsonProperty(PropertyName = "ext_ref", Order = 1)]
public string ExtRef { get; set; }
[JsonProperty(PropertyName = "external_comment", Order = 1)]
public string ExternalComment { get; set; }
[JsonProperty(PropertyName = "internal_comment", Order = 1)]
public string InternalComment { get; set; }
[JsonProperty(PropertyName = "object2")]
public Object2 Object2 { get; set; }
[JsonProperty(PropertyName = "object3")]
public Object3 Object3 { get; set; }
}
</code>
This is how I get it atm. This is fine for most of the objects, but not all:
{
"data": {
"attributes": {
"ext_ref": "2573421",
"external_comment": "Ext Comment",
"internal_comment": "Internal comment",
"object2": {
"data": {
"attributes": {
"xx":"XX",
"yy":"YY"
},
"id": "1",
"type": "object2s"
},
"object3": {
"data": {
"attributes": {
"xx":"XX",
"yy":"YY"
},
"id": "1",
"type": "object3s"
}
},
},
"type": "object1"
}
Because the object2 is a "special case", where it can only be understood by the server as a link, it needs to look like this:
{
"data": {
"attributes":{
"ext_ref": "2573421",
"external_comment": "Ext Comment",
"internal_comment": "Internal comment",
"object3": {
"data": {
"attributes": {
"xx":"XX",
"yy":"YY"
},
"id": "1",
"type": "object3s"
}
},
"type": "object1",
"relationships":{
"object2": {
"data": {
"id": "1",
"type": "object2s"
}
}
}
}
Now my question is this: Is there an easy way of doing this?
I have tried the following:
Using the Relationship attribute from JsonApiSerializer
Changing the JsonProperty settings for the object2.id property
Deleting the object2.id
I can't help thinking there must some attribute I can use to get the desired result, but atm. I am stuck
[EDIT]
I added an example object structure
Ok, I found the error. I am using Newtonsoft.Json to create the Json with this call:
string json = JsonConvert.SerializeObject(order, Format.None,
new JsonApiSerializerSettings {
NullValueHandling = NullValueHandling.Ignore
});
The part that caused the problem was the Format.None, which made the Json come out as basic Json, and not the usual format. I changed it to null, and I got the result I wanted. Big woop, wanna fight about it?

c# How do I parse a non array array

I need to parse a jsonfile that has a structure like this:
"number": {
"1": {
"branch": null,
"build": 1,
"files": [
[
"zip",
"client",
"4d96d6e8f1543c5fa1184f4771ce16e2"
],
[
"zip",
"src",
"fd397591148fac49a7d57aafdccac6a3"
],
[
"zip",
"server",
"d7e1df9a91ded33be81ee8226b027c2f"
],
[
"txt",
"changelog",
"df98aec1a868ce99532c64f246387d55"
]
],
"jobver": "1.3.2",
"mcversion": "1.1",
"modified": 1328269373.0,
"version": "1.3.2.01"
},
"2": {
"branch": null,
"build": 2,
"files": [
[
"zip",
"server",
"80fbd5d837a5867c2dd7b7967e3aa2a9"
],
[
"zip",
"client",
"0cedb5e9844e490f877b6cf04601f929"
],
[
"txt",
"changelog",
"87e9fba9322e9dbc2ea482a2c3edeec6"
],
[
"zip",
"src",
"292d5596879bd13c159a2afe571ec5eb"
]
],
"jobver": "1.3.2",
"mcversion": "1.1",
"modified": 1328613907.0,
"version": "1.3.2.2"
}
(The entire json file can be found here: http://files.minecraftforge.net/maven/net/minecraftforge/forge/json)
I have figure out the internals, in the "1" and the externals that leads to this code part, however using the Json.NET library I can't figure out how to deal with the "1" and "2"s without writing a seperate class for each. I have over 1000 of these to parse trough, so I need some way that can do it relatively simple.
My current class for parsing looks something like this:
public class forgemaven
{
public string homepage { get; set; }
public string name { get; set; }
public List<Number> number { get; set; }
public string webpath { get; set; }
}
public class Number
{
public string branch { get; set; }
public int build { get; set; }
public List<String> files { get; set; }
public string jobver { get; set; }
public string mcversion { get; set; }
public string modified { get; set; }
public string version { get; set; }
}
Note: I removed some of the things, like the first 1000 lines of version/build, since they aren't needed and json.net seems fine with that.
I have tried parsing it with http://json2csharp.com/ sadly that crashes my webbrowser. I also tried the desktop version of it, but that give me almost a MB of classes, which again isn't optimal, since new builds are constantly added to this file, so I would have to change my structure all the time, and it would be horrible to use the data from here.
So for my question: How do I do this in a maintainable way?
Your problem is that {"1" : something, "2" ; something} is not a json array, it's a json map.
In .net, you will want to deserialize that to a Dictionary<int,TObject>. Then you can process it afterwards if it really should be a `List' or an array, but you can't do that while deserializing: you got bad json in the first place, you have to deal with it.
Here is a fiddle demonstrating the thing (with the json you gave and your own classes only very slightly modified: the files property is a List<List<string>> instead of a List<String>) using Json.Net : https://dotnetfiddle.net/feqFZd
You must replace
public List<Number> number { get; set; }
with
public Dictionary<int,Number> number { get; set; }

C# List<List<T>>

I have a class with the following property: Education.cs
Apologize for the perhaps basic question
public List<List<Education>>data { get; set; }
public class Education
{
public From school { get; set; }
public From year { get; set; }
public string type { get; set; }
}
This is the class I have defined for deserializing json string
From is another .cs file
public string id { get; set; }
public string name { get; set; }
Here is my json string
education": [
{
"school": {
"id": "107751089258341",
"name": "JNTU, Kakinada, India"
},
"year": {
"id": "132393000129123",
"name": "2001"
},
"type": "College"
},
{
"school": {
"id": "103710319677602",
"name": "University of Houston"
},
"year": {
"id": "113125125403208",
"name": "2004"
},
"type": "Graduate School"
}
]
Can someone tell me how to access the members of Education(school, year)? It could be a piece of cake for you.
In my aspx.cs,
I have to write a foreach or any other to access my variables, school.name, year.name
Will have to work this access of class members into my aspx.cs
url= "https://graph.facebook.com/me?fields=education&access_token=" + oAuth.Token;
json = oAuth.WebRequest(oAuthFacebook.Method.GET, url, String.Empty);
List<Education>??? = js.Deserialize<List<??>(json)
Thanks
Smitha
You need two foreach loops inside of eachother; one for each level of List<>.
#Slaks solution should work for you. Though I believe your data is better represented as List<Education> (or better still, IEnumerable<Education>) and what you may want to do is to Flatten it out.
It would be better to flatten it out at the source, to make sure your code is cleaner at other places.
If you are on .NET 3.5 you can do it like
var flattenData = data.SelectMany(x => x);
If you are on pre .NET 3.5 / C# 3.0, you can do it like this
//Your original code block
{
IEnumerable<Education> flattenData = FlattenMyList(data);
//use flatten data normally
}
IEnumerable<T> FlattenMyList<T> (List<List<T> data){
foreach(List<T> innerList in data){
foreach(T item in innerList){
yield return item;
}
}
yield break;
}

Categories