I have an web API function that has a property that can take different shapes. Actually i need to save a json in database.
I will give you some examples
post body
{
"title":"example 1",
"type": "0",
"extraData": { "name": "bob",
// here is the catch this is first type of object let's say children
[{
"age": 10
"toys": "bear, goat"
},
{
"age": 18
"toys": "guitar"
}]
}
}
{
"title":"example 1",
"type": "1",
"extraData": { "name": "john",
// here is the catch this is first type of object let's say grandparents
[{
"age": 90
"cars": "honda civic"
},
{
"age": 18
"car": "renault megan, pegeout 206"
}]
}
}
Now let's assume that we can't merge grandparents and children together in a class so my classes will look something like:
public class Family{
public string Title {get; set;}
public string ExtraData {get; set;}
public int Type {get;set;}
}
public class ExtraData<T> where T: Person{
public string Name{get;set;}
public List<T> Persons {get;set;}
}
public class Person{
public int Age {get;set;}
}
public class Child : Person{
public string Toys{get;set;}
}
public class Grandparent : Person{
public string Cars{get;set;}
}
In the api method I was thinking to do something like:
public void Save(Family model)
{
// to parse extraData my idea was to
switch(mode.Type)
{
case 0: var childrens = JsonConvert.DeserializeObject<ExtraData<Child>>(model.ExtraData);
// do stuff
break;
case 1: var grandparents = JsonConvert.DeserializeObject<ExtraData<Child>>(model.ExtraData);
// do stuff
break;
}
// save the object
}
The issue is that on the api call I get nothing in the extraData string. And I can't put in Family something like
public ExtraData<Person> Persons {get;set;}
because it will deserialize to the base type and I will lose the subclass information.
I know is to do something like json stringify but I would like this to be my last resort. Also i have tried to use dynamic instead of Family but I still need to deserialze it and having same issue.
What is the best approach here?
Related
I need to inject the specific property value to object that is being deserialize using JsonConvert.DeserializeObject method.
for example I have a class
public class Employee
{
public int EmployeeID {get; set;}
public string Name {get; set;}
public int OrgnizationID {get; set;}
}
Json
[
{
"employeeID": 1,
"name": "Neeraj"
},
{
"employeeID": 2,
"name": "Sam"
},
{
"employeeID": 3,
"name": "Jonson"
}
]
above json string converting to list of employee. Here I am looking a way to set the OrgnizationID with some value for full of list. I know I can set it after conversion, but god to have if I can set it along with conversion.
var employees = JsonConvert.DeserializeObject<List<Employee>>(jsonData);
in above line of code I am also passing the JsonSerializerSettings for some other purpose that not defined here just to keep question simple.
How to avoid a property name during serialization? Below an example
public class A {
public List<object> Values { get; set; }
public string Name { get; set; }
}
A a = new A();
a.Name = "Numbers"
a.Values = new List<object>();
a.Values.Add(1);
a.Values.Add(2);
a.Values.Add(3);
a.Values.Add(4);
JsonConvert.SerializeObject(new { a });
After serialization, result includes a property names "values"
{
"a": {
"values": [
1,
2,
3,
4
],
"name": "Numbers"
}
}
but, I need the following
{
"a": [
1,
2,
3,
4
],
"name": "Numbers"
}
Just serialize a.Values;
JsonConvert.SerializeObject(a.Values);
You have a couple mistakes in your code.
First, if you want this class:
public class A
{
public List<object> Values { get; set; }
public string Name { get; set; }
}
To serialize to this json:
{
"a": [
1,
2,
3,
4
],
"name": "Numbers"
}
You are gonna have a bad time. Just look at the structure, they are not 1-1.
You need to change your class, make sure you name it something meaningful (a class of a single letter is a really bad idea).
public class SomeClass
{
[JsonProperty("a")]
public List<object> Values { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
You notice the [JsonProperty()] attribute above? That tells the serializer that this property should serialize with the defined name. That way you can have a meaningful property name ("Values") while in code, and still have the json serialize the way you want it to ("a").
The second problem is when you serialize you are creating a new anonymous object with a property of the class you instance you created. Which will again mess your structure up. Change your serialization code to just serialize the object instance:
string json = JsonConvert.SerializeObject(someInstanceOfYourClass);
My above changes should give you json that looks like this:
{
"a": [
1,
2,
3,
4
],
"name": "Numbers"
}
I have made a fiddle here that will demonstrate.
I'm trying to convert a string of JSON data into a C# class object. However I'm having an issue with a small part of the JSON which is dynamic in nature.
The part of the JSON is below:
"contact": [{
"comment": null,
"type": {
"id": "cell",
"name": "Example name"
},
"preferred": true,
"value": {
"country": "7",
"formatted": "+7 (702) 344-3423-3",
"number": "3498908",
"city": "702"
}
},
{
"type": {
"id": "email",
"name": "Email example"
},
"preferred": false,
"value": "name#mail.com"
}]
C# classes
public class Value
{
public string country { get; set; }
public string formatted { get; set; }
public string number { get; set; }
public string city { get; set; }
}
public class Type
{
public string id { get; set; }
public string name { get; set; }
}
public class Contact
{
public string comment { get; set; }
public Type type { get; set; }
public bool preferred { get; set; }
public string value { get; set; }
}
C# Code
Contact contact = JsonConvert.DeserializeObject<Contact>(result);
The format of "value" changes depending on the contact information. Is it possible to map value both as a string and also class Value.
Thanks for any help that can be provided.
You can literally just use dynamic, i.e.
public dynamic value { get; set; }
If it looks like an object, it will be materialized as a JObject, which can be used via the dynamic API, so .value.country will work, etc. If it looks like an integer, bool or string: it will be materialized as such. Arrays will also be handled suitably. So: you can check whether .value is string, etc. Note that this won't use your Value type, and doing so is more complex, but: meh; you get the data. You can always switch that out manually.
It will also behave like this if you use object instead of dynamic, but then it is harder to access the inner properties.
Try
Contact contact = JsonConvert.DeserializeObject<Contact>(result[0]);
As you can see in the JSON, it's
"contact": [
Indicating an array, currently you're just passing the entire array
Unless you're sure that the JSON comes always with the same structure, the best is to use a dynamic variable instead of deserialize it into a class.
If you like to work with classes you can always build your own on runtime using reflection. But that's like killing a fly with a cannon and you're probably won't need it, so just use a dynamic variable instead, it's the best to work with JSON strings.
I have the following json data:
{
"Persons": [
{ "Person": { "FirstName":"A", "Surname": "B" } },
{ "Person": { "FirstName":"C", "Surname": "D" } },
{ "Person": { "FirstName":"E", "Surname": "F" } }
]
}
My class definitions are:
public class PersonContext
{
public IDbSet<Person> Persons { get; set; }
}
public class Person
{
public string FirstName { get; set; }
public string Surname { get; set; }
}
When I use fastJSON to deserialize it to an object, I receive a NullReferenceException.
PersonContext context = fastJSON.JSON.ToObject<PersonContext>(jsonText);
Debugging source code of fastJSON, the exception is thrown on CreateGenericList, because the col variable of the method after calling (IList)Reflection.Instance.FastCreateInstance(pt); is null. So after, when it tries to add the parsed dictionary, it raises the exception.
Can I configure serialization process to ensure that IDbSet<T> objects are correctly processed?
Thanks in advance.
PS: You can also clone my repo in github in order to reproduce this issue.
This is the answer for my question by mr. Mehdi Gholam (fastJSON owner) on this thread:
fastJSON needs concrete types and not interfaces to be defined so it knows what type to create.
So change your code to :
public class PersonContext
{
public List<Person> Persons { get; set; }
}
Here are my Class
[DataContract(Name="Test")]
public class Test
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Type { get; set; }
}
[DataContract(Name="Root")]
public static class Root
{
[DataMember(Name="TestList")]
public static List<Test> TestList { get; set; }
}
Expected Json To be returned
{
"Test":[
{
"Name": "MyApp",
"Type": "web"
},
{
"Name": "MyDatabase",
"Type": "db"
}
]
}
Actual Json Returned
[
{
"Name": "MyApp",
"Type": "web"
},
{
"Name": "MyDatabase",
"Type": "db"
}
]
WebApi Method to return the objects
[HttpGet]
public IEnumerable<Test> Get()
{
return Root.TestList;
}
The problem i am facing is when i run the above code I see the json data being returned in the "Actual" Format but i would love to see the Json in the "Expected Format" (please see above for the formats). The only difference is the label of the array. How can i put this label? i looked at tons of json docs but no luck. Please help.
Your method is returning a List<Test> so that will be serialized as a JSON array. If you want to see a JSON object with a named array-valued property, you need to return a POCO containing an appropriately named property, such as your Root:
[HttpGet]
public Root Get()
{
return Root;
}
Also, you need to change the name from TestList to Test:
[DataContract(Name="Root")]
public class Root
{
[DataMember(Name="Test")] // Changed this
public List<Test> TestList { get; set; }
}
Or, if your Root contains other properties you don't want serialized, or in other ways can't be serialized (because it's static), you can always return some generic wrapper, like so:
[DataContract]
public class RootWrapper<T>
{
[DataMember(Name = "Test")]
public T Test { get; set; }
}
And then
[HttpGet]
public RootWrapper<IEnumerable<Test>> Get()
{
return new RootWrapper<IEnumerable<Test>> { Test = Root.TestList };
}