C# JSON parse instead of R - c#

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.

Related

How to properly navigate structure of Json file

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.

Alexa skill relative path to JSON file

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?

NewtonSoft.Json error

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.

Default entries on a first time creation for a serialized class

I have a class I am using for serializing various configuration options for an application I am working on. I'm adding a new property to the class that is a List, and I'd like it to fill this list if it does not exist already in a XML file. My first thought was to check if the list contained zero items, however this is not acceptable because there are times I want to have zero items in the list. In essence I want a file that has been serialized with an older version of the same class to be "upgraded" and have defaults automatically inserted for new properties. How can I do this? For a more visual example of what I'm trying to do, see below:
When I deserialize an XML file that contains:
<Item1>wtfe</Item1>
<Item2>wtfe</Item2>
and after I've added a list property it will serialze as:
<Item1>wtfe</Item1>
<Item2>wtfe</Item2>
<Item3/>
I want it to serialize as:
<Item1>wtfe</Item1>
<Item2>wtfe</Item2>
<Item3>
<DefaultSubItem/ Field="wtfe">
<DefaultSubItem/ Field="wtfe">
</Item3>
But allow me to change it to:
<Item1>wtfe</Item1>
<Item2>wtfe</Item2>
<Item3></Item3>
Another option may be to use these attributes:
[OnSerializing()]
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.onserializingattribute.aspx
[OnDeserializing()]
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializingattribute.aspx
I think your looking for the SerializationBinder class:
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder%28VS.71%29.aspx

Problem deserializing validated XML, can't convert to/from array

I'm a bit out of my element. I've used xsd.exe to create an xsd schema from an xml file, and then to create a C# class from that xsd. That all seems to work fine.
At the moment I'm just trying to get the XML deserialized. The file I'm deserializing if the very same file I used to build the class originally. Here's my code:
String xsdPath=#"C:\Users\tol56881\Documents\dnd4e.xsd";
String xmlPath=#"C:\Users\tol56881\Documents\dnd4e.xml";
String xsdNamespace="";
//Validation stuff
XmlParserContext context = new XmlParserContext(null, null, "", XmlSpace.None);
XmlValidatingReader vr = new XmlValidatingReader(xmlPath, XmlNodeType.Element, context);
vr.ValidationType = ValidationType.Schema;
vr.Schemas.Add(xsdNamespace, xsdPath);
while (vr.Read()) ;
//Actually reading the file
TextReader tr = new StreamReader(xmlPath);
D20Character character = (D20Character)(new XmlSerializer(typeof(D20Character))).Deserialize(tr);
It compile fine, but when I try to run it I get the an error that's repeated for four different objects. I've given an example below, changing the names of the objects.
Unable to generate a temporary class (result=1).
error CS0030: Cannot convert type 'Namespace.ObjectName[]' to 'Namespace.ObjectName'
error CS0029: Cannot implicitly convert type 'Namespace.ObjectName' to 'Namespace.ObjectName[]'
So it seems like the program is trying to go from array to object and back to array, but I'm not really sure. The auto-generated class code is a huge mess that's difficult to wade through. I'm hoping that maybe there's something simple I'm missing here.
Thanks!
I managed to fix this. Each of the four objects in question were generated as doubly-indexed arrays, such as:
private loot[][] lootTallyField;
and
public loot[][] LootTally
{
get
{
return this.lootTallyField;
}
set
{
this.lootTallyField = value;
}
}
All I did was remove one set of brackets, and it all seems to be working fine. No problems with deserialization and a quick inspection of the deserialized object makes it look like the data was loaded correctly.
private loot[] lootTallyField;
and
public loot[] LootTally
{
get
{
return this.lootTallyField;
}
set
{
this.lootTallyField = value;
}
}
Still not sure why xsd.exe made these doubly-indexed if they're not supposed to be. I feel like I'm still missing something, hence why this question is still open.
Particularly, if I ever need to re-generate this code, then I'd need to reapply the fix, which kind of defeats the purpose of using a partial class in the first place...
There is a problem on xsd.exe tool, I will try to explain.
If you have a complexType with a sequence inside that has a child complexType with a sequence and the first one does not have any other elements / attributes, then the generated class will have only 1 generated type, instead of 2 and it will be a double array.
If you make the double array into a single array, you will be able to deserialize your xml just fine.
HOWEVER this will produce the following unexpected result.
If your xml looks like the below.
<root>
<loot>
<tally>value1</tally>
<tally>value2</tally>
</loot>
<loot>
<tally>value3</tally>
<tally>value4</tally>
</loot>
</root>
Then your deserialized object, in the lootTally array would only contain the value3 and value4 items instead of having all 4.
So you have 2 options to fix this correctly:
Alter the xsd file by adding a dummy in the first sequence, and run xsd.exe again, so that when it generates the class it will not create a double array, and then you can delete the dummy attribute from the class.
Alter the generated class, add a new class named loot which will contain an array of tally objects which you already have (and only need to alter the name).
Please note that in option 2 you may have to change some declarations if you have an XmlArrayItemAttribute to XmlElementAttribute.
Hope this helps

Categories

Resources