Converting dynamic element string in JSON into a class - c#

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.

Related

Get property default value as expression using reflection

In order to build a custom transpiler, I'm trying to get the default value of all the properties inside a class as an expression, and not as a value itself.
Let me bring you some examples to clarify what I'm trying to do and what I've done/tried/investigated so far.
Source code could be the following one:
const string DATETIME_NOW = "____DATETIME_NOW____";
public class Person {
[DefaultValue("Foo")]
public string Name { get; set; } = "Foo";
[DefaultValue(DateTime.Now)] // This is not doable: "An attribute argument must be a constant expression"
public DateTime DateOfBirth { get; set; } = DateTime.Now;
[DefaultValue(DATETIME_NOW)]
public string DateOfBirthStringed { get; set; } = DATETIME_NOW; // which acts like DateTime.Now.ToString()
}
The ultimate goal of the transpiler, is to obtain a Javascript class that looks like this:
class Person {
name: string = "Foo";
dateOfBirth: Date = new Date(Date.now());
dateOfBirthStringed : Date = Date.now();
}
My current, and working, implementation is the use of DefaultValue attribute with some constants strings used when the default value is an expression (e.g. DateOfBirthStringed).
What I'm doing is using reflection on Person, getting all the PropertyInfo, looking for their DefaultValue attribute, and then checking if the given default value are some fixed constants like DATETIME_NOW.
This works, but I've a couple of problems:
The type in attribute DefaultValue could be different from the type of the property.. No type check :(
If I only have the DefaultValue, when I write new Person(), the default values are not actually set from the attribute.
Therefore, I need to write the default value after { get; set; }, but:
Or I wrote both attribute and default value, but then I should manually mantain synchronized them.
I write only the default value, but then I've no way to get it with reflection.
About point 3.2, why I can't get the default value via reflection?
Suppose the Person class defined above; if I use reflection, I need to instantiate it, but once instantiated, Person.DateOfBirth has an actual DateTime value, and I cannot know it was coming from DateTime.Now.
Also, if Person would be abstract.. Well, no way to instantiate it.
So, basically, the only way I could perfectly transpile the code is to read the code as a tree, something like parseTreeCode(typeof(Person)). At that point, I should navigate the tree and be able to do everything I want.
I did find Roslyn, which allows me to parse C# code, but.. It parses "stringed" code, and not code that belongs to the same project. I thought "well, get the file where Person is defined, and parse the whole file", but unfortunately, once the program is running, I cannot get the file where Person is defined.. I mean, I can do typeof(Person).Assembly, and getting the Assembly.. But it would be an assembly, so it would not be good.
At this point, I'm thinking that there is no real solution for this, but maybe I'm missing some C# packages/features that could help me

Deserializing Pokemon API in .NET

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".

C# Deserializing JSON to class dependent on a type property

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.

How can you deserialize a JSON array containing different types in .Net?

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.

Generate code based which populate an object

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.

Categories