Noob junior dev here trying to fix some old code written by someone else more senior but left. This person had a json file with a structure like this:
A: {
B: {
C: stuff1,
D: stuff2,
E: stuff3
}
},
...
The old code deserializes the json file into a JObject then tries to get information from the child node like this:
stuff1 = jObject["C"];
When I run the code, it just fills stuff1 with null. When I use the whole path like this:
stuff1 = jObject["A"]["B"]["C"]
I get the information I need from the child node. Just wondering if this was an oversight on the original author's part or if there's something I'm not seeing.
Am I right that you shouldn't be able to access the information in the child node without navigating through the tree properly? Is there any more robust way to get the information from the child node directly? I can see a scenario where if the structure of the Json file changes, then the jObject["A"]["B"]["C"] will no longer work.
If you're using Json.NET it looks like a bug or json changed.
However you address the situation write a unit test that shows what json(s) the code is expected to work with. The next dev will be have it easier; and that next dev may be you in 6 months.
Dealing with changing json
I can see a scenario where if the structure of the Json file changes, then the jObject["A"]["B"]["C"] will no longer work.
Dealing with changing json is ... let's say hard. If the json libary you're using supports JSONPath you could try with '$..C'. If you're using Json.NET have a look at Querying JSON with LINQ.
Fixing the bug
Use jObject["A"]["B"]["C"] or deserialise to a class and move on.
Related
I have a little problem with my Alexa skill. I want to include display components in it, and I want to send an appropriate directive. For most of my code, I use custom classes from API, e.g.
Resources colorsDark = new Resources();
colorsDark.description = "Color for dark theme";
colorsDark.when = "${viewport.theme == 'dark'}";
However, for one part of my skill, I use only previously-created values, so there is no need to create new objects and assign values to them. Instead, I've created a .json file that includes all the necessary information.
I'd like to point my code to this file, but here I encountered an issue.
I'd like to make it look like this:
doc.Styles = [JSON_FILE]
However, when the function is executed, it can't find this file.
I'm using JObject from Newtonsoft.Json.
I tried to use only the relative path:
JObject jObject = JObject.Parse(File.ReadAllText(".\\AlexaPresentationLanguage\\Styles\\ListStyle.json"));
as well as some other solutions like
Path.GetCurrentDirectory
and
Path.Combine
From System.IO
So far nothing worked. Do you have any ideas what can I do?
Suppose I have a nested json file which looks like this:
[{"toplevel":{"firstleveldata":{"id":12345,"no":123}},"secondleveldata":{"fruit":{"apples":"y"},"veg":{"small":{"gree":{"fresh":{"available":3}}}}},"thirdleveldata":{"fruit":{"changes":[{"itemid":1,"subno":1,"green":[],"red":[{"extraid":2,"element":5}]}]}}}]
and the following R code which can parse the it to a nice data.frame except for the last one (if that can be fixed to that's a bonus)
what would be neat altenative using C# look like? It's not something i've ever coded in before so wouldn't know where to start.
The end goal being to export the nested JSON into a csv, multiple csvs with primary / foreign keys to merge on of necessary.
R code for context.
library(jsonlite)
library(tidyverse)
fun <- function(x)
{list(
#keys
id = pluck(x,"toplevel","firstleveldata","id", .default = NA),
no = pluck(x,"toplevel","firstleveldata","no", .default = NA),
apples = pluck(x,"secondleveldata","fruit","apples", .default = NA),
itemid = pluck(x,"toplevel","thirdleveldata","fruit","changes","itemid", .default = NA) #doesn't work)}
out<-map_df(list.files("my_json_file",full.names=TRUE),~map_df(fromJSON(txt=., simplifyVector=FALSE), fun))
out
Thanks
A quick way to explore this structure and get started with json in C# is to copy the json packet as you expect it to be. Taking what you have in your question as an example, copy the json into the clipboard and create a new C# code file and write your default namespace only as follows:
namespace Data
{
// Next step will be here
}
After that, place your cursor between the curly braces, click the edit menu, then paste special, JSON as Classes
This will generate some classes for you that match the JSON schema you have. In this case, there's a Class1 because it cannot determine the name of the object with the toplevel, secondlevel etc properties. You can give it a more meaningful name for your case.
Update for .NET 5.0 and later versions
Following the release of .NET 5.0, a new namespace now comes out of the box that can handle JSON as easily as Newtonsoft and in a performant way.
The simplest way to parse the Json into your objects will be with a one liner as follows...
var items = JsonSerializer.Deserialize<IEnumerable<MyItem>>(jsonData);
For this to work, you'll need to have the following in your usings...
using System.Collections.Generic; // For IEnumerable<T>
using System.Text.Json; // For JsonSerializer.*
Following is the original answer that used the Newtonsoft.Json Nuget package. That still works with .NET 5.0 but I've included the new namespace for those who would wish to avoid including an extra DLL that has functionality that is covered out of the box.
Original method using Newtonsoft.Json
Open the package manager console (if you can't see it in VS, press Ctrl+Q and type "Package Manager Console"). In the package manager, type the following
Install-Package Newtonsoft.Json
after ensuring that you have your solution saved and specifying the project you want to add that package to as your default project as in the screenshot:
and wait for it to complete. You'll now have the ability to deserialize JSON you have into your classes. Go to where you want to do the processing and type the following...
// at the top of the file...
using Newtonsoft.Json;
// where you need to decode it...
string jsonData = GetYourJsonDataFromAFileOrAPICall(); // replace with how you get the json
var items = JsonConvert.DeserializeObject<IEnumerable<MyItem>>(jsonData);
At this point, you'll have an IEnumerable<MyItem> (MyItem is what I renamed Class1) representing your json. You can change that to be a list, or array if you want a more specific collection.
I have a json returned from the Facebook SDK in unity that looks like this:
{"name":"Name_of_person", "id":"989988988"}
I've taken a look at the raw JSON and this is what it looks like after I submit a query for that information via the Graph API.
So far I am trying to deserialize the json as such:
void DealWithProfileInfo(IGraphResult result){
Dictionary<string,string> profile = JSON.Deserialize (result.RawResult) as Dictionary<string,string>;
scoresText.text = profile ["first_name"];
idText.text = profile["id"];
DealWithProfileInfo being my callback method from the FB.API call.
for some reason none of the information gets displayed and I am not sure why.
Am I missing something? Or maybe I am parsing the data wrong?
Actually it is my personal experience that whenever I mess up with Facebook SDK or any other JSON in Unity3d, then what ever the hierarchy of JSON would be, it always returns Dictionary<string,object>. So developer have to come through that stairs
So in your case you should declare and cast to Dictionary<string,object> instead of Dictionary<string,string> then convert that object to string.
Hi I'm quite new to C# and I'm trying to make a text editor that saves and loads Plaintext formats. I've used NewtonSoft.Json NuGet package, but I'm getting an error. I've stated a string called textToLoad, which is set to a JsonConvert.DeserializeObject. Only thing is, it says it can't convert an object to a string! I tried toString(); but it still had the same error.
It is kind of hard without the code. The process of serializing and deserializing is pretty straight forward using Json.Net. So this is an example from their documentation:
YourType yourObject= new YourType();
yourObject.Property="something";
string output = JsonConvert.SerializeObject(yourObject);
//For some reason you want this to be string, but is the type you serialized in the first place
YourType textToLoad= JsonConvert.DeserializeObject<YourType>(output);
This outlines the basic works of serializing and deserializing. But we don't really know the details of your implementation.
Hope it helps.
You can't deserialize into a string like that. At simplest form you started with JSON in the form of:
{ value: "someString" }
If you want something out of it, you must deserialize and then get the value from it.
dynamic foo = JsonConvert.DeserializeObject<dynamic>(theJson);
var textToLoad = foo.value.ToString();
You must deserialize to something in order to inspect and get properties from it.
[Edit] - Perhaps I'm not understanding. But if you share code, I'll update my answer.
I have the idea of serializing the properties of a class to a XML-file and then upload this file to a server. This is because I want to download this file again and read out the serialized content.
So a structure like this:
File exists (myfile.xml) -> Serialize data from a class (myclass.cs) to the file -> Upload the file.
Now the content could look something like this:
<?xml version="1.0"?>
-<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeProperty>0.1.0.0</SomeProperty>
<SomePropertiesChild>Test</SomePropertiesChild>
</MyClass>
Well, so this is not the problem, I have built a class to serialize the data to the file, so that it looks like above.
The problem now is, that I do not know how to go on. I want that the next time new data will be serialized like that, but it should be attached to that XML-file, not a new one.
So I download this file and then serialize new data as attachment to it, so that it will look the following:
<?xml version="1.0"?>
-<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeProperty>0.1.0.0</SomeProperty>
<SomePropertiesChild>Test</SomePropertiesChild>
<!-- This is the part that should be attached -->
<SomeProperty>0.2.0.0</SomeProperty>
<SomePropertiesChild>Test2</SomePropertiesChild>
</MyClass>
And this should be repeated as many times as I want: download file, serialize new data and attach it to this one, then upload it again.
Now, the second point is, that I want to read this out.
For that I use the XML-class, too. For the type param for the type that the data should be serialized to, I set the class that contains the properties.
But now, I have more nodes of the same name in my XML like above. I want to separate this now.
So, not only that one class, but for each node with the same name a new class.
To show you that a bit clearer:
In the XML we have the first pair (SomeProperty, SomePropertiesChild).
This one should be deserialized to myclass.cs now, which contains the right properties for the XML-nodes. Example:
public class MyClass
{
public Version SomeProperty {get; set;}
public string SomeProeprtiesChild {get; set;}
}
And then the first XML-pair should be deserialized to that class.
But I want to do this for each XML-"pair" in my XML-file, but I can't get a right structure in my head.
Should I make a List here or how should I do that?
I really hope I could explain it a bit clearly, it is very difficult to explain.
Also I do not know how to do this with the attaching. Any tips?
If you have questions, ask!
What you are looking for is not simply serialization. You want to serialize the same object more than once right?
A possible solution is to serialize a List<MyClass> and append new instances to the list if the file exists.
In short, there is no built-in way to do what it is you are looking for. However, that doesn't mean it can't be done...
The simplest, and closest built-in, way would be to maintain a List<MyClass> which you update whenever you upload/download etc. and serialize. However, this wouldn't get the exact XML you want, this would produce something along the lines of
<ListOfMyClass>
<MyClass>
<Property>1</Property>
...
</MyClass>
<MyClass>
<Property>2</Property>
...
</MyClass>
</ListOfMyClass>
The more complex approach would be to have MyClass implement IXmlSerializable and override the behaviour of WriteXml/ReadXml to handle your custom XML format.