I have a list of Team that I am trying to deserialise.
class Team
{
public string TeamName {get; set;};
private List<FootballPlayer> _fPlayers = new List<FootballPlayer>();
public List<FootballPlayer> FPlayers
{
get => _fPlaters;
}
}
class FootballPlayer
{
private Team _team;
public string Name { get; set; }
public Team Team
{
get => _team;
}
[JsonConstructor]
public FootballPlayer(Team team)
{
_team = team;
}
}
I have the following JSON settings:
JsonSerializerSettings serializerSettings = new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
Formatting = Formatting.Indented
};
When I look at the serialised output, it appears correct, and the references between nodes are properly represented.
{
"$id": "1",
"TeamName": "Test",
"FPlayers": [
{
"$id": "2",
"Team": {
"$ref": "1"
},
"Name": "Leo Messi"
}
]
}
When the data is deserialised, the property "Team" in the player "Leo Messi" is null.
How can I deserialise this JSON in such a way that the property "Team" of "Leo Messi" is not null?
JsonConvert.DeserializeObject<List<Team>>(JsonString, serializerSettings);
Related
I have the following minimal example:
class Program
{
static void Main()
{
var plan = new Plan
{
Steps =
{
new Step
{
Contexts =
{
new Context
{
Name = "1"
},
new Context
{
Name = "2"
}
}
}
}
};
var settings = new JsonSerializerSettings
{ PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented };
var json = JsonConvert.SerializeObject(plan, settings);
var deserialized = JsonConvert.DeserializeObject<Plan>(json, settings);
}
}
class Plan
{
public IEnumerable AllContexts => Steps.SelectMany(i => i.Contexts);
[JsonProperty(Order = int.MaxValue)]
public ICollection<Step> Steps { get; set; } = new List<Step>();
}
class Step
{
public ICollection<Context> Contexts { get; set; } = new List<Context>();
}
class Context
{
public string Name { get; set; }
}
In this example deserialized has lost its references upon deserialization and deserialized.AllContexts is a collection of 2 null values.
I can get this working by changing [JsonProperty(Order = int.MaxValue)] to [JsonProperty(Order = int.MinValue)] so the Steps are serialized first - but in my scenario I want the actual JSON to have all its properties on the flat AllContexts array and for the Steps to only have $refs like this:
{
"$id": "1",
"AllContexts": [
{
"$id": "2",
"Name": "1"
},
{
"$id": "3",
"Name": "2"
}
],
"Steps": [
{
"$id": "4",
"Contexts": [
{
"$ref": "2"
},
{
"$ref": "3"
}
]
}
]
}
This seems like a bug in JSON .NET - is there a way to work around it?
I have for example these classes
public class JsonResult
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("myobjects")]
public List<BaseClass> myObjects { get; set; }
}
public abstract class BaseClass
{
[JsonProperty("name")]
public string Name { get; set; }
}
public class FlavourOne: BaseClass
{
[JsonProperty("number")]
public int Number { get; set; }
[JsonProperty("mytext")]
public string MyText { get; set; }
}
public class FlavourTwo: BaseClass
{
[JsonProperty("yourtext")]
public string YourText { get; set; }
}
And i have this Json object coming in as a string
{
"name": "examplename",
"myobjects": [
{
"name": "myFlavourOneInstance",
"number": 1,
"mytext": "Text from One"
},
{
"name": "myFlavourTwoInstance",
"yourtext": "Your Text from Two"
}
]
}
I would like to have an object from that Json string as follows
var responseObject = JsonConvert.DeserializeObject<JsonResult>(rawJsonString);
But this does not work, is there a nice and clean way to have the json into the object with inherited classes and so on?
Yes, you can specify the class in the Json for deserialization using TypeNameHandling.Auto (or TypeNameHandling.All, depending on your use case).
Here's an example using TypeNameHandling.Auto:
void Main() {
var jsonResult = new JsonResult {
Name = "test",
myObjects = new List<BaseClass> {
new FlavourOne {
Name = nameof(FlavourOne),
Number = 1,
MyText = "Text from one"
},
new FlavourTwo {
Name = nameof(FlavourTwo),
YourText = "Text from two"
}
}
};
var serializerSettings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto
};
var json = JsonConvert.SerializeObject(jsonResult, Formatting.Indented, serializerSettings);
Console.WriteLine(json);
}
// Your classes here
Output:
{
"name": "test",
"myobjects": [
{
"$type": "FlavourOne, AssemblyName",
"number": 1,
"mytext": "Text from one",
"name": "FlavourOne"
},
{
"$type": "FlavourTwo, AssemblyName",
"yourtext": "Text from two",
"name": "FlavourTwo"
}
]
}
This means that when deserialized, they are assigned the correct type, as long as you use the same serializer settings:
var responseObject = JsonConvert.DeserializeObject<JsonResult>(json, serializerSettings);
Note though, as per the docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source.
This is because you don't want to let external Json create arbitrary types!
I have this JSON:
[
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 1",
"Values": [
"Acc 1"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "1",
"Values": [
"1"
]
}
}
],
"Name": "account",
"Id": "1"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 2",
"Values": [
"Acc 2"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "2",
"Values": [
"2"
]
}
}
],
"Name": "account",
"Id": "2"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 3",
"Values": [
"Acc 3"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "3",
"Values": [
"3"
]
}
}
],
"Name": "account",
"Id": "2"
}
]
And I have these classes:
public class RetrieveMultipleResponse
{
public List<Attribute> Attributes { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}
public class Value
{
[JsonProperty("Value")]
public string value { get; set; }
public List<string> Values { get; set; }
}
public class Attribute
{
public string Key { get; set; }
public Value Value { get; set; }
}
I am trying to deserialize the above JSON using the code below:
var objResponse1 = JsonConvert.DeserializeObject<RetrieveMultipleResponse>(JsonStr);
but I am getting this error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'test.Model.RetrieveMultipleResponse' because the type requires a JSON
object (e.g. {"name":"value"}) to deserialize correctly. To fix this
error either change the JSON to a JSON object (e.g. {"name":"value"})
or change the deserialized type to an array or a type that implements
a collection interface (e.g. ICollection, IList) like List that can
be deserialized from a JSON array. JsonArrayAttribute can also be
added to the type to force it to deserialize from a JSON array. Path
'', line 1, position 1.
Your json string is wrapped within square brackets ([]), hence it is interpreted as array instead of single RetrieveMultipleResponse object. Therefore, you need to deserialize it to type collection of RetrieveMultipleResponse, for example :
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
If one wants to support Generics (in an extension method) this is the pattern...
public static List<T> Deserialize<T>(this string SerializedJSONString)
{
var stuff = JsonConvert.DeserializeObject<List<T>>(SerializedJSONString);
return stuff;
}
It is used like this:
var rc = new MyHttpClient(URL);
//This response is the JSON Array (see posts above)
var response = rc.SendRequest();
var data = response.Deserialize<MyClassType>();
MyClassType looks like this (must match name value pairs of JSON array)
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class MyClassType
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "Description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "Manager")]
public string Manager { get; set; }
[JsonProperty(PropertyName = "LastUpdate")]
public DateTime LastUpdate { get; set; }
}
Use NUGET to download Newtonsoft.Json add a reference where needed...
using Newtonsoft.Json;
Can't add a comment to the solution but that didn't work for me. The solution that worked for me was to use:
var des = (MyClass)Newtonsoft.Json.JsonConvert.DeserializeObject(response, typeof(MyClass));
return des.data.Count.ToString();
Deserializing JSON array into strongly typed .NET object
Use this, FrontData is JSON string:
var objResponse1 = JsonConvert.DeserializeObject<List<DataTransfer>>(FrontData);
and extract list:
var a = objResponse1[0];
var b = a.CustomerData;
To extract the first element (Key) try this method and it will be the same for the others :
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync("Your URL"))
{
var apiResponse = await response.Content.ReadAsStringAsync();
var list = JObject.Parse(apiResponse)["Attributes"].Select(el => new { Key= (string)el["Key"] }).ToList();
var Keys= list.Select(p => p.Key).ToList();
}
}
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
worked!
I can't seem to find a good answer so far, but I acknowledge maybe I'm just not smart enough to know the right keywords to search. So here goes.
Suppose I have a collection containing mixed object types:
var wishList = new List<WishListItem>
{
new Car { Price = 78000, Make = "Tesla", Model = "S", Name = "Tesla Model S" },
new Computer { Manufacturer = "Microsoft", Name = "Surface Pro 6", Price = 2000 },
new PlatitudeIdea { Name = "World peace" }
};
As a collection built in memory, I can use casting to handle these objects according to their underlying types:
foreach (var wishListItem in wishList)
{
if (wishListItem is PlatitudeIdea platitude)
{
Console.WriteLine($"{platitude.Name} is a hopeless dream");
}
else if (wishListItem is IPriceable priceThing)
{
Console.WriteLine($"At {priceThing.Price}, {priceThing.Name} is way out of my budget");
}
else
{
Console.WriteLine($"I want a {wishListItem.Name}");
}
}
If I serialize it as a JSON array, everything looks fine...
[
{ "Price": 78000, "Make": "Tesla", "Model": "S", "Name": "Tesla Model S" },
{ "Manufacturer ": "Microsoft", "Name": "Surface Pro 6", "Price": 2000 },
{ "Name": "World peace" }
]
... but when I parse the JSON, the parser obviously can't tell exactly which type each element was originally, so it just tries to parse them as the lowest level type declared in the List's generic parameter (WishListItem) as I would expect:
parsedWishList[0] is WishListitem // returns true :)
parsedWishList[0] is Car // returns false :(
This makes sense, and you could get this behavior any time the member being serialized is declared as a super type or interface. What I would love to be able to do is add a special property to my specific classes, indicating the type of the object being serialized:
public class Car : WishListItem, IPriceable
{
public override string #type => "Car";
}
Or better yet, as a type attribute:
[JsonSerializedType("Car")]
public class Car : WishListItem, IPriceable
{
// ...
}
This would then be output to the JSON whenever the type declaration is ambiguous...
[
{ "type": "Car", "Price": 78000, "Make": "Tesla", "Model": "S" },
{ "type": "Computer", "Manufacturer ": "Microsoft", "Name": "Surface Pro 6", "Price": 2000 },
{ "type": "Platitude", "Value": "World peace" }
]
... And the parser would deserialize that object as that type:
parsedWishList[0] is Car // returns true :)
The closest thing to an answer i was able to glean from Google was maybe to try playing with the CustomCreationConverter and see if that might help somehow. But I need a very generic answer I can write once and let it handle arbitrary types.
Any pointers?
It sounds like you are looking for the TypeNameHandling setting. This setting will cause Json.Net to write type information into the JSON so that it is deserialized back to its original type.
If you need to customize the type names, you can use a custom SerializationBinder class.
Here is a round-trip demo based on the KnownTypesBinder sample shown in the documentation:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace SO54465235
{
public class Program
{
public static void Main(string[] args)
{
var wishList = new List<WishListItem>
{
new Car { Price = 78000, Make = "Tesla", Model = "S", Name = "Tesla Model S" },
new Computer { Manufacturer = "Microsoft", Name = "Surface Pro 6", Price = 2000 },
new Platitude { Name = "World peace" }
};
KnownTypesBinder knownTypesBinder = new KnownTypesBinder
{
KnownTypes = new List<Type> { typeof(Car), typeof(Computer), typeof(Platitude) }
};
string json = JsonConvert.SerializeObject(wishList, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = knownTypesBinder
});
Console.WriteLine(json);
Console.WriteLine();
List<WishListItem> items = JsonConvert.DeserializeObject<List<WishListItem>>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = knownTypesBinder
});
foreach (var wishListItem in wishList)
{
if (wishListItem is Platitude platitude)
{
Console.WriteLine($"{platitude.Name} is a hopeless dream");
}
else if (wishListItem is IPriceable priceThing)
{
Console.WriteLine($"At {priceThing.Price}, {priceThing.Name} is way out of my budget");
}
else
{
Console.WriteLine($"I want a {wishListItem.Name}");
}
}
}
}
public class KnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
class WishListItem
{
public string Name { get; set; }
}
interface IPriceable
{
int Price { get; set; }
string Name { get; set; }
}
class Car : WishListItem, IPriceable
{
public string Make { get; set; }
public string Model { get; set; }
public int Price { get; set; }
}
class Computer : WishListItem, IPriceable
{
public string Manufacturer { get; set; }
public int Price { get; set; }
}
class Platitude : WishListItem
{
}
}
Output:
[
{
"$type": "Car",
"Make": "Tesla",
"Model": "S",
"Price": 78000,
"Name": "Tesla Model S"
},
{
"$type": "Computer",
"Manufacturer": "Microsoft",
"Price": 2000,
"Name": "Surface Pro 6"
},
{
"$type": "Platitude",
"Name": "World peace"
}
]
At 78000, Tesla Model S is way out of my budget
At 2000, Surface Pro 6 is way out of my budget
World peace is a hopeless dream
It's easier to show a mock up of the problem rather than trying to explain it first.
internal class Program
{
private static void Main(string[] args)
{
Class1 class1 = new Class1() { Name = "Scott" };
Class2 class2 = new Class2() { Name = "Steve", Objects = new List<Class1>() { class1 } };
Class2 class22 = new Class2() { Name = "Atanas", Objects = new List<Class1>() { class1 } };
List<Class2> list = new List<Class2>() { class2, class22 };
string jSonString = JsonConvert.SerializeObject(list,Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
List<Class2> result = (List<Class2>) JsonConvert.DeserializeObject(jSonString, typeof(List<Class2>));
if (result[0].Objects[0] == result[1].Objects[0])
{
Console.WriteLine("Correct, its the same object");
}
else
{
Console.WriteLine("Bah!, its a new object");
}
}
}
public class Class1
{
public string Name { get; set; }
}
public class Class2
{
public Class2()
{
Objects = new List<Class1>();
}
public List<Class1> Objects { get; set; }
public string Name { get; set; }
}
The problem is that when the string is deserialized, the "Shared Object" is now duplicated. Before being serialized, the same object (by reference) was in two separate lists. After de serializing both the lists contain separate objects.
Is it possible to have json behave so it doesn't duplicate?
Hope that makes sense
Steve
Yes, if you setup your serializer as follow:
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All
};
Json string will be:
{
"$id": "1",
"$values": [
{
"$id": "2",
"Objects": {
"$id": "3",
"$values": [
{
"$id": "4",
"Name": "Scott"
}
]
},
"Name": "Steve"
},
{
"$id": "5",
"Objects": {
"$id": "6",
"$values": [
{
"$ref": "4"
}
]
},
"Name": "Atanas"
}
]
}
And you will see in console:
Correct, its the same object
See http://james.newtonking.com/projects/json/help/index.html?topic=html/T_Newtonsoft_Json_PreserveReferencesHandling.htm