I am serializing objects to json using Json.NET. However, certain properties of my object are objects themselves that contain quite a lot of additional information, so my json output is very cluttered and hard to interpret. Ideally, I would like to show that these specific properties exist (show the object type) but without fully serializing them.
For example, instead of:
{
"sport": "football",
"popular": true,
"rules": [
{
"highSchool":
{
...
}
},
{
"college":
{
...
}
},
{
"nfl":
{
...
}
}
]
}
I would like to show:
{
"sport": "football",
"popular": true,
"rules": {Sports.Football.Rules}
}
Is this possible? I have tried using [JsonIgnore] in my Rules class for the rules property, but this ignores the rules property entirely instead of including but not serializing the property's object. I also read that [JsonProperty] and [JsonIgnore] are static and cannot be conditionally set, so that seems to rule out an alternative approach of selectively serializing the property (such as if Verbose is provided).
Related
I'm working on an ASP .NET core web api where I'm trying to create a model in C# based on a JSON that can have dynamic properties.
The type field can be of 3 types: Land, Water, Air.
Based on the type, the corresponding properties in the "source" would be different.
For example. if the type is "Land", there would be an additional property in source called "speed". If the type is "Air", there would be 2 additional properties in source called "Height" and "NumberLandings".
The modify property is an array that can have different types of modifications - Performance/Aesthetic/Functional...., and based on the modification type, I can have different properties underneath for that modification.
For example, if the type is "Performance", there would be additional properties such as brakes/turbo/suspension.
{
"properties": {
"source": {
"type": "Land",
"speed": "160mph"
},
"modify ": [
{
"type": "Performance",
"brakes": "",
"turbo": "",
"suspension": ""
},
{
"type": "Functional",
"electric": "",
"applications": "Carplay"
}
]
}
}
Question: How can I construct my C# class/model for the dynamic JSON? I'm thinking to keep the source type and modification type as an enum, and based on the enum type, define the other parameters.
I don't want to define all the properties and end up having some of them as null, like below:
[DataContract]
public class Source
{
[DataMember]
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[DataMember]
[JsonProperty(PropertyName = "speed")]
public string Speed{ get; set; }
[DataMember]
[JsonProperty(PropertyName = "height")]
public string Height{ get; set; }
[DataMember]
[JsonProperty(PropertyName = "NumberLandings")]
public string NumberLandings { get; set; }
}
The type field can be of 3 types: Land, Water, Air. Based on the type, the corresponding properties in the "source" would be different.
This is essentially polymorhism, and there are existing questions and articles regarding this. This might require a custom jsonConverter class, and it might be easier if you format your message to have a separate object, rather than loose properties, i.e:
"type": "Land",
"MyBaseObject":{
"speed": "160mph"
}
The modify property is an array that can have different types of modifications - Performance/Aesthetic/Functional...., and based on the modification type, I can have different properties underneath for that modification.
This sounds like a key-value store, and may use the same approach to polymorphism as above to store different properties for different kinds of objects, i.e. it could be represented by a Dictionary<string, MyModificationBase>.
Keep in mind that you still need to use the object model somehow, preferably without needing do typechecking all over the place.
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 have a scenario where I receive json as a string. Each piece of json is formatted the same, except for a collection of items within. Take this as simplified examples:
{
"Type": "A",
"Items": [
{ "Name": "Item 1" },
{ "Name": "Item 2" }
]
}
{
"Type": "B",
"Items": [
{ "Counter": 1 },
{ "Counter": 2 }
]
}
I have the following models in code to represent this:
public enum Type
{
A, B
}
public class InstanceTypeA
{
public string Name { get; set; }
}
public class InstanceTypeB
{
public int Counter { get; set; }
}
note: in the future, the types will be extended further, say to D, E, and F for this example. All of which will have an item collection structured differently again.
And I would like to deserialize this into a model, for example:
public class Container<T>
{
public Type Type { get; set; }
public List<T> Items { get; set; }
}
where T would be either instanceTypeA or instanceTypeB
As mentioned, I receive the json shown at the top as a string. And it's the "type" enum that dictates the type of the object held in the items collection. I'm struggling to find a very generic way to implement this serialization.
I was thinking the factory pattern could help but not sure how to implement this - has anyone tackled a similar problem.
In my code, I happen to know if it's type A, B etc before actually attempting serialization. I could do this:
switch(obj.Type)
{
case A: JsonConvert.DeserializeObject<Container<A>>(obj.Json)
case B: JsonConvert.DeserializeObject<Container<B>>(obj.Json)
}
The drawback of this approach is that the calling code needs to then know its dealing with Container<A> or Container<B> etc. Which isn't generic at all. I can't get A or B to implement a common interface as they are both completely different. Bit stumped with this - I was hoping Factory pattern could help but can't see how - has anyone dealt with a similar issue before?
Thanks for any pointers in advance!
You don't want to use the Type property to dictate what type is used. When using the Newtonsoft Json serializer, you can include the type of an object in the JSON document. That way, when the serializer deserializes the string, it will understand what types to use. So you could completely drop the Type property and just check the type of object in the array. I believe you need to set the TypeNameHandling property of the settings object passed into the serializer. Once you see the serializer outputting a $type property with your objects in the JSON string, you know that you've done it right.
I am trying to dynamically instantiate a class from my visual studio solution based on a JSON string.
Before I describe my exact issue I want to give an example of what I want to achieve.
Say I have the following JSON :
{
"Type": "AutoIncrementTag,
"StartFrom": 0,
"Skip": 10,
"LeadingZero": false
}
So from that Json, I want to find the class called "AutoIncrementTag" and instantiate it, setting its "StartFrom", "Skip" and "LeadingZero" parameters to the correspoding values.
Note 1: I have a couple of theese "Tag" classes and I want to instantiate a different on the "Type" attribute in my Json string.
Note 2: My Json string will contain more than 1 of these class "descriptions" (I believe they are called JSON Objects but I'm not too familliar with the JSON format just yet)
Note 3: I am using Newtonsoft.Json for all the Json parsing/converting.
So, now for my issue.
I managed to get the Type property using
JObject.Parse(myJsonString).GetValue("Type").ToString();
However, how would I go about getting all the other values, since they will be different depending on what Type I have? (I need a way to dynamically iterate and get the values of the other properties.)
And second, how do I then map these properties to a C# object.
I thought about using
Activator.CreateInstance(Type type, object[] args)
But how can I (dynamically) get an object[] from the properties described in my json format.
JSON.Net (i.e. Newtonsoft.Json) does this for you already. For example, lets start off with a basic class:
public class Thing
{
public int SomeValue { get; set; }
public string AnotherValue { get; set; }
}
And an instance of it:
var thing = new Thing { SomeValue = 5, AnotherValue = "blah" };
We can deserialise with a custom settings object, specifically setting the TypeNameHandling property
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
var json = JsonConvert.SerializeObject(thing, settings);
Which will give output something like this:
{
"$type":"Thing, Namespace",
"SomeValue": 5,
"AnotherValue": "blah"
}
And to get it back into the right kind of object, just use the same settings:
var anotherThing = JsonConvert.DeserializeObject(json, settings);
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.