I tried this piece of source code (based on NewtonSoft's JSON NuGet library), for reading a JSON file into a JSON object:
string str_File_Content = File.ReadAllText(openFileDialog1.FileName);
Rootobject existing_root = JsonConvert.DeserializeObject<Rootobject>(str_File_Content);
... and it almost worked: all JSON objects are loaded into the existing_root and in case of arrays, the number of objects seems to be correct.
But: the values of the attributes seem not to be filled in, as you can see here:
JSON file excerpt:
{
"project": {
"common.DESCRIPTION": "Some information",
existing_root excerpt in Watch window:
Expected :existing_root.project.commonDESCRIPTION : Some information
Real result :existing_root.project.commonDESCRIPTION : null
What can I do in order to make JsonConvert.DeserializeObject() not only handle the structure, but also the values?
Your json property name contains "." symbol, which is invalid for C# property name, so you can specify correct name to use during (de)serialization with JsonPropertyAttribute:
public class Project
{
[JsonProperty("common.DESCRIPTION")]
public string commonDESCRIPTION { get; set; }
}
Related
I'm attempting to deserialize the pokemon API located at https://pokeapi.co/api/v2/pokemon/3 and I'm relatively new to working with json. I've made a class and method that loads whatever pokemon I choose into an object but can only get it to work with the simple keys like "name: value" and "id: value"
class Pokemon
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
// [JsonProperty("abilities")]
// public Dictionary<string, string>[] Abilities { get; set; }
//[JsonProperty("types")]
// public Dictionary<string, int>[] Types { get; set; }
//[JsonProperty("sprites")]
//public Dictionary<string, string> Sprites { get; set; }
public static Pokemon LoadPokemon(int num)
{
string json = new WebClient().DownloadString($"https://pokeapi.co/api/v2/pokemon/{num}");
Pokemon pokemon = JsonConvert.DeserializeObject<Pokemon>(json);
return pokemon;
}
}
All of the fields I can't make work I've commented out. Basically my question is how do I make those fields I've commented out actually load. I've tried a combination of dictionaries, arrays, and lists as datatypes but I can't seem to make anything work.
"sprites": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/3.png",
"back_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/female/3.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/3.png",
"back_shiny_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/female/3.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/3.png",
"front_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/female/3.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/3.png",
"front_shiny_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/female/3.png",
"other": {
"dream_world": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/3.svg",
"front_female": null
},
"official-artwork": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/3.png"
}
},
'Unexpected character encountered while parsing value: {. Path 'sprites.other', line 1, position 189387.' This is my usual error message when I attempt to load sprites, and the other attributes have similar errors. I can see why the "other:" key wouldn't work with Dict<string, string> but I also don't know how to avoid that problem since not every key in sprites has that format.
Also I'm using NewtonSoft.Json and System.Net packages. You can find the abbreviated version of the API at https://pokeapi.co/.
Trying to figure out the correct C# structure for very complex JSON documents can be quite difficult. I would suggest you start by using one of the many online JSON to C# convertor tools (e.g. https://app.quicktype.io/ or Google "JSON to C#") to take a sample JSON document and generate your class models. Using the sample JSON from https://pokeapi.co/, I used the quicktype tool to instantly generate the C# models. You can then take that C# code and modify it to suit your needs (create separate files for each class, remove unneeded properties, etc). But at least this provides you with a good (and valid) starting point. Tools like quicktype have config options that allow you to to refine the models, such as if you want JSON arrays to be C# arrays or Lists, etc.
"sprites" in your example will resolve to a Dictionary<string, object>. You will then, in your code, have to cast the value to either string, or another Dictionary<string, object> in the case of "dream_world" and "official_artwork".
Let's say I have this little json snippet:
{
"Type": "Bar",
"BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}
I also have these three classes:
public class Base
{
public enum SampleEnum
{
Bar,
Baz,
}
public SampleEnum Type
{
get;
set;
}
}
public class Bar : Base
{
public string BarOnly
{
get;
set;
}
}
public class Baz : Base
{
public string BazOnly
{
get;
set;
}
}
Based on the Type property in the json snippet, I'd like to have it deserialize to either Bar or Baz.
My first idea was to first deserialize it to the Base class, and then use its type and a switch statement to deserialize the JSON again to its respective class. (Using Newtonsoft.Json)
var type = JsonConvert.DeserializeObject<Base>(json).Type;
string message = "";
switch (type)
{
case (Base.SampleEnum.Bar):
message = JsonConvert.DeserializeObject<Bar>(json).BarOnly;
break;
case (Base.SampleEnum.Baz):
message = JsonConvert.DeserializeObject<Baz>(json).BazOnly;
break;
}
Console.WriteLine(message);
Needless to say that this process is extremely redundant, tedious and, since the switch statement is hard-coded, not very "dynamic" at all.
Another idea was to use a generic class as the base class instead and passing in the type of the actual class it should deserialize to, but then I end up with the same switch statement to figure out what that class should be.
Since you can't map enums to class types, I also thought about using a Dictionary to map the possible enum values to their class counterparts; this still makes the mapping process hard-coded though.
Is there any way I can dynamically get the corresponding class to deserialize to based on the type property of the json object?
EDIT: There seems to be some confusion about how this is supposed to be used and how the data is fetched; let me provide some background information.
I'm iterating through a directory with a lot of different spreadsheet files, mostly CSVs and XML files. Each of these feeds have a "meta file", describing how to process their content. This includes checksums, delimiters and other information. They also declare of what type their parent file is (CSV, XML etc). Hence, they share a lot of common properties (like the Base class in my example), but also have their own set of properties. They derive from an abstract class that requires them to implement a function that returns an instance of the corresponding feed processing class, initialized with values directly from within the meta class. I hope this makes sense.
#OguzOzgul commenting is correct. I've done this countless of times for objects that are composed with interfaces that need to be serialized and deserialized.
See TypeNameHandling for Newtonsoft:
https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm
Your json file will look ever so slightly different:
{
"$type": "SomeNamespace.Bar",
"BarOnly": "This is a string readable when deserialized to the Bar class only, as declared in my type key"
}
If you use
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
}
During serialization, it will add the full type name of all objects to make sure newtonsoft knows what their type is during deserialization (given you use the same settings then). That way you do not have to write your own custom code for type detection.
I Use JsonConvert to serialize an object and save it in a database. This is a sample of the serialized string that I saved in database:
[{"matId":"1","value":"44"},{"matId":"2","value":"55"},{"matId":"4","value":"77"}]
Now when I get this string from database which has a lot of backslashes like this:
"[{\"matId\":\"1\",\"value\":\"44\"},{\"matId\":\"2\",\"value\":\"55\"},{\"matId\":\"4\",\"value\":\"77\"}]"
And for this reason I can't Deserialize it.
.Replace("\\","") method doesn't create any affect on this. I don't know why.
You have to use JsonConvert.Deserialize method.
Your json string is wrapped within square brackets ([]), hence it is interpreted as array. Therefore, you need to deserialize it to type collection of one class, for example let's call it MyClass.
public class MyClass
{
public int matId { get; set; }
public int value { get; set; }
}
Here is Deserialize method.
var results=JsonConvert.DeserializeObject<List<MyClass>>(json);
Backslashes represent serialized object.
You need to deserialize your List object.
You can try using Generics:
public List<T> Deserialize<T>(string path)
{
return JsonConvert.DeserializeObject<List<T>>(path);
}
Be careful when looking at json strings as you are debugging. In Visual Studio it needs to format the value into a string. To do that it will add " so that it can process it when actually the value does not contain them. That is why the replace does not work.
I want to access some address of pictures in a JSON, but the field name is a number and in c# a number is not a valid name for a variable...
My JSON:
{
"id":3441,
"name":"test",
"address": {
"1":"url.com\/45.jpg",
"2":"url.com\/23.jpg",
"3":"url.com\/65.jpg",
"4":"url.com\/789.jpg",
},
"count":2
}
My code in C#: (HrmlResult is my JSON)
dynamic stuff1 = Newtonsoft.Json.JsonConvert.DeserializeObject(HtmlResult);
string address= stuff1.address; //It Works
string allPics = stuff1.pic; //It Works
firstPicTextBox.Text= stuff1.pic.1; //compiler Error
secondPicTextBox.Text = stuff1.pic[2]; //runtime Error
What should I do?!
Thank you all...
You have to create Model object with properties, as in found json you're expecting.
Then, for number properties, you can use JsonProperty attribute to name the property as number, for example:
class MyModel {
[JsonProperty("2")]
public string Two {get; set;}
}
and then use DeserializeObject<MyModel> version
This is simplified example, for your object you have to maintain the hierarchy and probably have another class for 'address' property and use it as property type in the main model.
If you're able to modify the JSON, that's your best bet. What you have seems like it should be in an array:
{
"id":3441,
"name":"test",
"address":[
"url.com\/45.jpg",
"url.com\/23.jpg",
"url.com\/65.jpg",
"url.com\/789.jpg"
],
"count":2
}
From there, your address is a simple array of strings, rather than a numbered key-value pair collection.
I have a slight situation. I'm interacting with a web service using RestSharp, where the service is requiring me to send the following as part of the request:
{
"a":"a value",
"b":"b value"
}
Which is all fine and dandy, because you could simply use a class such as this:
public class MyClass
{
public string A { get; set; }
public string B { get; set; }
}
However, I do not know know the property names at runtime. Therefore, I attempted to use an ExpandoObject, but of course, this simply serialized as a JSON array:
[
"a":"a value",
"b":"b value"
]
So, it would seem that I need to be able to serialize (and deserialize) a Dictionary (or IEnumerable<KeyValuePair<string, string>>) as a JSON object (in other words, use curly braces instead of a brackets).
Does anyone know how I might do this, preferably by using a Json.NET attribute, such that the functionality may be reused elsewhere?
how about using a JObject?
var obj = new JObject();
obj["One"] = "Value One";
obj["Two"] = "Value Two";
obj["Three"] = "Value Three";
var serialized = obj.ToString(Formatting.None);
gives you
{"One":"Value One","Two":"Value Two","Three":"Value Three"}
Use JavascripSerializer object from .net class libs. It supports reflection on the object it is serializing
see msdn docs