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".
Related
My primary languages growing up were PHP, Python, etc. In these languages it is very easy to initialize dictionary-style objects (so-called associative arrays in other languages).
I am using C# for a project now and find the method of initializing a large dictionary to be quite cumbersome and figured I may be doing something wrong, or against best practices. For example, here is a dictionary I want to keep some project data in:
//Campaign dictionary
public Dictionary<int, Dictionary<string, Dictionary<object, Dictionary<string, object>>>> campaignData = new Dictionary<int, Dictionary<string, Dictionary<object, Dictionary<string, object>>>>();
It seems like there is a better way to do this. The code is not readable at all and just to initialize a simple 3-4 stage hierarchy is a massive line of code. If you know of a way, please let me know! Thank you
EDIT ** I have drawn out the type of structure I am trying to obtain. Game progress data that can be bundled and written to a save file:
Don't use dictionaries for everything. A dictionary is essentially a collection of values or objects identified by unique keys. Unless that's specifically what you're using, a dictionary is the wrong tool.
Instead, consider the semantics of the structure you want and build that structure. For example, in the image in the question you have:
An Objective, which contains a Vector3 and a bool
A list of Objectives
A Level, which contains a list of Objectives and a bool
So something like this:
public class Objective
{
public Vector3 Position { get; set; }
public bool IsCompleted { get; set; }
}
public class Level
{
public IEnumerable<Objective> Objectives { get; set; }
public bool LevelIsComplete { get; set; }
}
Maybe you further have a list of levels, contained perhaps within a "game" or something of that nature. Etc.
The point is that C# embraces static typing in classes, where you're trying to use very loose typing in associative arrays. Build the structures you want into classes, include the necessary logic within those classes where that logic semantically belongs, and the code will read a lot better.
I never encountered such a multi-level map structure but if you want to do it cleaner, you need to define those map structures as types. This makes the code both readable (in terms of declaration length) and understandable.
Of course you would use sensible names for the types and not TypeOutermost.
internal class Program
{
public static void Main(string[] args)
{
var campaignData = new Dictionary<int, TypeOutermost>();
}
public class TypeOutermost : Dictionary<string, TypeMid>
{
}
public class TypeMid : Dictionary<object, TypeInnermost>
{
}
public class TypeInnermost : Dictionary<string, object>
{
}
}
I'm trying to deserialize a JSON object array in C#. The array represents a row of a table, mainly consisting of plain strings. However, one or more of the items in the array may not be a string but infact a JSON object in itself, e.g.
"rows":[[{"date":"20140521","category":"one"},"data","moredata","evenmoredata"],...]
or on a different response from the server, the order may be different
"rows":[["data","moredata",{"date":"20140521","category":"one"},"evenmoredata"],...]
I'm trying to just treat this as a list of objects, with a known type called RowObject below:
[DataContract]
[KnownType(typeof(RowObject))]
public class Table
{
// other members to deserialize
[DataMember(Name = "rows")]
public List<List<object>> Rows { get; set; }
}
[DataContract]
public class RowObject
{
[DataMember(Name = "date")]
public DateTime date { get; set; }
[DataMember(Name = "category")]
public string category { get; set; }
}
This approach kind of worked in that the plain strings in the row were deserialized, however the RowObjects do not seem to be recognised even though I have tried to put them down as a KnownType. When I look at my deserialized List<object> Row, the RowObject just seems to be a blank object shown as {object} in the debugger.
I've managed to do this with known types and derived types elsewhere in the project but this problem dealing with plain strings has got me stuck. I've had a look at this question which seems pretty similar, but my problem with this answer is that I don't know which elements in the list are going to be the complex type. Also, I'm just using the .Net DataContractJsonSerializer throughout the project so would like to avoid third party libraries if at all possible.
Is it possible to deserialize a JSON array of different types like this?
Set EmitTypeInformation in DataContractJsonSerializerSettings to EmitTypeInformation.Always on the server side. That way you will get information about the types of your objexts, inside the json string.
I could not find a question that deals with what I am about to ask so I went ahead and created this question.
I have a json coming back to me from our UI and I can convert that to an object without any issues until I hit this line:
\"lang\":{\"en-US\":{\"name\":\"AS Test Assembly Activity\",\"description\":\"Activity to test assembly activities\"}}
My problem is the "en-US" it appears it would be a class with fields of 'name' and 'description'. How can I safely convert the "en-US" to an object? This can be dynamic and will be whatever the culture code is set too.
I was able to get it converted by changing it to this:
\"lang\":{\"culture\":\"en-US\",\"name\":\"AS Test Assembly Activity\",\"description\":\"Activity to test assembly activities\"}}
But now the UI is stating they could use this, but prefer it to be inline with the original json.
Any thoughts?
Thanks
Edit: (What so I mean 'convert "en-US" to a class?)
when I convert this to a class it should look simliar to this:
public class CustomActivityLanguageData
{
public string Name { get; set; }
public string Description { get; set; }
public string Culture { get; set; }
}
But the Culture was added for the 'fix' I put in above. On the original json I posted, it looks like "en-US" would be a class in the same way that the 'CustomActivityLanguageData' class in code above is a class (btw 'CustomActivityLanguageData' == 'lang' in json)
It appears "en-US" has fields attached to it, like a class but it won't convert.
Double Edit: To further expound on this, it appears this is what the class would ultimately look like:
public class Lang
{
en-US cultureCode {get;set;}
}
public class en-US
{
public string name {get;set;}
public string description {get;set;}
}
Hope that explains a little better.
You need to realise what you are getting here. You received a dictionary (in JSON, it is called an 'object'). One of the keys in the dictionary is "lang". The value for the "lang" key is itself a dictionary. It could have many keys, probably for different languages, but you received only one key named "en-US". And the value for that key is again a dictionary with two keys "name" and "description".
Normally I would expect that you could receive multiple dictionaries, not just "en-US" but keys for other languages like French or German or Japanese; you would then pick the one that is most appropriate for your application, and extract the "name" and "description" keys from it.
What limitations are there on data types used in a class that will be used by SQLite-net to represent a table? Specifically, can I use this:
public List<string> CoconutWaterBrands { get; set; }
...or will I need this:
public string[] CoconutWaterBrands { get; set; }
...or something else altogether?
Similar to Sandy's Answer, in terms of serializing/deserializing the list, you could use Text blobbed properties from the SQLite-Net Extensions library. So for example in your Model class:
public class SomethingToDoWithCoconuts
{
[TextBlob(nameof(CoconutWaterBrandsBlobbed))]
public List<string> CoconutWaterBrands { get; set; }
public string CoconutWaterBrandsBlobbed { get; set; } // serialized CoconutWaterBrands
}
from the documentation on Text blobbed properties:
Text-blobbed properties are serialized into a text property when saved and deserialized when loaded. This allows storing simple objects in the same table in a single column.
Text-blobbed properties have a small overhead of serializing and deserializing the objects and some limitations, but are the best way to store simple objects like List or Dictionary of basic types or simple relationships.
Text-blobbed properties require a declared string property where the serialized object is stored.
Text-blobbed properties cannot have relationships to other objects nor inverse relationship to its parent.
A JSON-based serializer is used if no other serializer has been specified using TextBlobOperations.SetTextSerializer method. To use the JSON serializer, a reference to Newtonsoft Json.Net library must be included in the project, also available as a NuGet package.
ORMs (aka abstraction leak) will have this kind of problem. The fields in the class will correspond to columns in the table. If you use List or arrays then the ORM will either have to somehow combine those values in the list in a single column, for that it will have to choose some kind of separator and what if the separator appears inside your string value, ultimately you will slowly find your self in rabbit hole.
I don't think SQLite-net support arrays/list. You will need to use string (and split and join it as required) or create a new table to represent the one-to-many relationship.
To provide a more concrete solution to the question, you could use Newtonsoft.Json to serialize to a JSON blob:
I.e.
private string coconutWaterBrands;
[Ignore]
public List<string> CoconutWaterBrands {
get
{
return JsonConvert.DeserializeObject<List<string>>(coconutWaterBrands);
}
set
{
coconutWaterBrands = JsonConvert.SerializeObject(value);
}
}
Let's say I have the following classes.
public class MyClass {
public string Data1 { get; set; }
public MyOtherClass Data2 { get; set; }
// 50+ other properties...
}
public class MyOtherClass {
public string OtherData1 { get; set; }
// More properties
}
There's somecode that instanciate that class and populates it with all the data. I'd like to use that object for my test. I could simply serialize the structure into XML and reload it later. However, what I would really like is to have the entire object tree build in the code. In other words:
MyClass myClass = new MyClass {
Data1 = "Hello",
Data2 = new MyOtherClass {
OtherData1 = "World",
// More...
},
// More...
}
I could write all that myself, but it would take hours and be error prone since there's a high number of properties and sub-classes. Here's my question: given an object how would you generate the code which populate that object?
I would write a T4 template. Check out an example that is doing something, although really remotely, similar to what you need.
I would use json for a data format and use something like http://json2csharp.com to generate classes to use to serialize and deserialize to and from json. Or given the classes already existing annotate them and serialize them out.
This will handle any arbitrary nesting and be maintainable. Values can even be edited without a recompile which is usually a good thing. The link also leads to examples for how to specify specific types, handle enums, object links, etc.
Perhaps if you specify why it absolutely has to be generated from code only we can give better answers.