NewtonSoft.Json JsonConvert Deserialize error - c#

I have serialized a complex object( containing abstract classes, read only properties) using Newtonsoft.Jsonconverter SerializeObject successfully.
While trying to Deserialize the same using DeserializeObject method, it throws following error
- An item with this key has already been added.
On further investigation I found out that there might be some properties in the object with same name. But I couldn't find any property name being repeated in json file being de-serialized.
Version of NewtonSoft Json : 8.0.3

Hi I was trying to replicate your error but actually what I got was a successful result. this is the test I did :
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var child = new Child();
var serializesObject = JsonConvert.SerializeObject(child);
var deserializedObject = JsonConvert.DeserializeObject(serializesObject, typeof(Child));
}
}
public abstract class Abstract
{
public int Prop1 { get; set; }
public readonly string Prop2;
public List<string> Prop3 { get; set; }
public int[] Prop4 { get; set; }
public abstract void Hey();
public Abstract()
{
Prop1 = 1;
Prop2 = "2";
Prop3 = new List<string>();
Prop4 = new int[4];
}
}
public class Child : Abstract
{
public readonly string Prop5;
public Child()
{
Prop5 = "5";
}
public override void Hey()
{
throw new NotImplementedException();
}
}
}
I hope that this code can help you to get to the expected result you want.
Cheers,

Related

How to deserialize json to a Dictionary<string,object> in a class using NetwornSoft.JsonConvert.DeserializeObject<T> in C#

I'm having a problem with the deserialization of json to a class with a Dictionary<string,object> using Newtonsoft's JsonConvert.DeserializeObject() function.
Currently what's happening is it's just deserializing a string to the object value, where the string is the json for the class that was serialized.
The dictionary property in the class that's being serialized to JSON:
[Serializable]
public abstract class EntitySaveData
{
[Header("Settings")]
[SerializeField, Tooltip("A unique server-side identifier to bind EntitySaveData with EntityGameData and Entity components")]
[JsonProperty("id")]
protected string id = string.Empty;
[JsonProperty("lvl")]
public int Level { get; protected set; }
[JsonProperty("hp")]
public int CurrentHitPoints { get; protected set; }
[JsonIgnore]
public string Id => id;
[JsonProperty("bsd")]
public Dictionary<string, object> behaviourSaveDataByName = new Dictionary<string, object>();
}
The class for the object that's being added to the dictionary
[JsonObject(MemberSerialization.OptIn), Serializable]
public class SaveDataGeneratorBehaviour
{
[JsonRequired, JsonProperty("cap")]
private SaveDataCapacity saveDataCapacity;
public SaveDataCapacity SaveDataCapacity => saveDataCapacity;
[JsonConstructor]
public SaveDataGeneratorBehaviour(float currentAmountStored)
{
saveDataCapacity = new SaveDataCapacity(currentAmountStored);
}
public void UpdateAmountStored(float currentAmountStored)
{
saveDataCapacity.UpdateAmountStored(currentAmountStored);
}
}
The class nested in the class that's being saved. (Another employee did this, I don't know why).
public class SaveDataCapacity : SaveDataBehaviour
{
[JsonRequired, JsonProperty("s")]
private int currentAmountStored;
[JsonRequired, JsonProperty("t")]
private DateTime timeSaved;
public int CurrentAmountStored => currentAmountStored;
public DateTime TimeSaved => timeSaved;
[JsonConstructor]
public SaveDataCapacity(float currentAmountStored)
{
this.currentAmountStored = Mathf.RoundToInt(currentAmountStored);
timeSaved = DateTime.Now; // TODO: (Seb) Replace with server time
}
public void UpdateAmountStored(float currentAmountStored)
{
this.currentAmountStored = Mathf.RoundToInt(currentAmountStored);
timeSaved = DateTime.Now; // TODO: (Seb) Replace with server time
}
}
The method used to deserialize the object
private static T TryDeserialize<T>(object obj, T deserializedObject)
{
try
{
deserializedObject = JsonConvert.DeserializeObject<T>(obj.ToString());
}
catch (Exception e)
{
ChaosTheoryGames.Core.Logger.LogWarning(e.Message);
}
return deserializedObject;
}
The full JSON of the object where the EntitySaveData is nested in (In some kind of compressed format):
{\"buildings\":[{\"grid\":{\"x\":8,\"y\":9},\"state\":{\"s\":\"StateBuildingIslandIdle\",\"t\":0.0,\"end\":\"2022-01-05T21:31:33.978876+11:00\"},\"id\":\"Engine\",\"lvl\":1,\"hp\":500},{\"grid\":{\"x\":11,\"y\":10},\"state\":{\"s\":\"StateBuildingIslandBuilding\",\"t\":7.99000072,\"end\":\"2022-01-05T21:31:51.4714003+11:00\"},\"id\":\"Solar Panel\",\"lvl\":1,\"hp\":400,\"Gen_SD\":{\"cap\":{\"s\":0,\"t\":\"2022-01-05T21:31:42.4754811+11:00\"}}}]}
I've tried so many different attributes and such and I can't get it to work. I've read about using JsonConverters but I feel like it shouldn't be necessary for this problem.
Any advice would be greatly appreciated!

DeserializeObject<T>(string s) in generic method in C# using Newtonsoft.JSON is not working

Currently I am just returning the json string to corresponding file from where Test1() is called and Deserializing there as ResponseClass r = JsonConvert.DeserializeObject(response_json)
Send part I forget to make a class [Serializable].Its working fine now.
Part1:
public class Movie
{
public string Name { get; set; }
public string Description { get; set; }
public string Classification { get; set; }
public string Studio { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> Genres{ get; set; }
}
public class ResponseClass
{
public string SuccessStatus{ get; set; }
public string next_link { get; set; }
}
private void Test1<T,Q>()
{
string json = #"{
'Name': 'Bad Boys',
'ReleaseDate': '1995-4-7T00:00:00',
'Genres': [
'Action',
'Comedy'
]
}";
//Here making network call with above json and getting correct response_josn
Q response_obj = JsonConvert.DeserializeObject<Q>(reponse_json);
print(response_obj);
}
I am calling Test1() as follows on button click:
Test1<Movie, ResponseClass>();
For the above example I am getting print log as ClassName+Movie (T FullName).
I want to deserialize the string into that class. How to achieve that?
Part2 : If I have class as:
[Serializable]
public class Movie
{
public string Name;
public string Description;
public string Classification;
public string Studio;
public DateTime ReleaseDate;
public SubClass subClass;
public List<SubClass> lsubclass;
}
[Serializable] //This was the mistake.
public class SubClass
{
public string a;
public string b;
public List<string> ReleaseCountries;
}
private Movie createValidMovieJson()
{
Movie m = new Movie();
SubClass sc = new SubClass();
sc.a = "aaa";
sc.b = "bbb";
sc.ReleaseCountries = new List<string>();
sc.ReleaseCountries.Add("Japan");
sc.ReleaseCountries.Add("India");
List<SubClass> lsC = new List<SubClass>();
lsC.Add(sc);
lsC.Add(sc);
m.Name = "Bad Boys";
m.Studio = "Pixa";
m.subClass = sc;
m.lsubclass = lsC;
Debug.Log(JsonUtility.ToJson(m)); // value n log = {"Name":"Bad Boys","Description":"","Classification":"","Studio":"Pixa"}
return m;
}
JsonUtility is returning empty value in place of subclass after using ToJson() as shown in above function.
Based on the screenshot you added I think you are expecting to be able to treat the deserialized type as a Movie. This is the way to achieve that:
var movie = JsonConvert.DeserializeObject<Movie>(json);
Currently your deserialized object is being treated as type T - which could be anything since you have no generic type constraints on your method.
Like I said in the comment section, JsonUtility should do it.
I just replaced T m = JsonConvert.DeserializeObject(json); with T
m = JsonUtility.FromJson(json); it gives an error
ArgumentException: JSON parse error: Missing a name for object member.
Your json is invalid for JsonUtility. I believe you are using ' instead of ". This is why you are getting this error.
Use the function below to generate a valid json:
void createValidMovieJson()
{
Movie m = new Movie();
m.Name = "Bad Boys";
m.ReleaseCountries = new List<string>();
m.ReleaseCountries.Add("Japan");
m.Studio = "Pixa";
Debug.Log(JsonUtility.ToJson(m));
}
You will get:
{"Name":"Bad Boys","Description":"","Classification":"","Studio":"Pixa","ReleaseCountries":["Japan"]}
When ecaped for testing, you will get:
{\"Name\":\"Bad Boys\",\"Description\":\"\",\"Classification\":\"\",\"Studio\":\"Pixa\",\"ReleaseCountries\":[\"Japan\"]}
For JsonUtility to work, you must add [Serializable] to the class and remove { get; set; } from them class variables.
If your goal is to convert any json to any data type then you have to return generic type then use Convert.ChangeType to convert it to that type.
It should look something like this:
// Use this for initialization
void Start()
{
string json = "{\"Name\":\"Bad Boys\",\"Description\":\"\",\"Classification\":\"\",\"Studio\":\"Pixa\",\"ReleaseCountries\":[\"Japan\"]}";
Movie movie = Load<Movie>(json);
print(movie.Name);
}
[Serializable]
public class Movie
{
public string Name;
public string Description;
public string Classification;
public string Studio;
public DateTime? ReleaseDate;
public List<string> ReleaseCountries;
}
private T Load<T>(string json)
{
object resultValue = JsonUtility.FromJson<T>(json);
return (T)Convert.ChangeType(resultValue, typeof(T));
}

Blank array serialized when serializing to XML

I am trying to serialize two List objections to SQL Server.
This is my serialization code:
public static string SerializeToXml<T>(this T value)
{
var writer = new StringWriter(CultureInfo.InvariantCulture);
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(writer, value);
return writer.ToString();
}
When the following property is serialized and then saved, everything looks fine:
this.IPHostList = liveHostIps.SerializeToXml<List<string>>();
But when I try this with a custom class, I do not get any data.
this.MyClassList = MyClassContainer.SerializeTpXml<List<MyClass>>();
As an example of what I see in my database for the failed item:
<ArrayOfMyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
An example of what I see from the successful item:
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>192.168.1.4</string>
<string>192.168.1.5</string>
</ArrayOfString>
I tried serializing to XML for the benefit of viewing the data in the database, but if needed I can just convert to base64.
Any thoughts on why my custom class is not being saved? My custom class is:
[Serializable]
public class MyClass
{
public bool prop1{get;set;}
public Nullable<System.Guid> prop2 {get;set;}
public string prop3 {get;set;}
}
Note that I get no code errors - just that my xml array stored in the database is nonexistent.
I reproduced your code and ran it through a unit test but it seems to serialize properly. Here's my entire code. Maybe looking through this will help. Put a break on Assert.IsNotNull and you'll see that the serialization appears to be properly handled.
using System;
[Serializable]
public class MyCustomClass
{
public bool prop1 { get; set; }
public Nullable<Guid> prop2 { get; set; }
public string prop3 { get; set; }
}
using System.Globalization;
using System.IO;
using System.Xml.Serialization;
public static class MySerializer
{
public static string SerializeToXml<T>(this T value)
{
var writer = new StringWriter(CultureInfo.InvariantCulture);
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(writer, value);
return writer.ToString();
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var myCustomClasses = new List<MyCustomClass>();
myCustomClasses.Add(new MyCustomClass { prop1 = true, prop2 = Guid.NewGuid(), prop3 = "Testing" });
myCustomClasses.Add(new MyCustomClass { prop1 = true, prop2 = null, prop3 = "Testing2" });
var serialized = MySerializer.SerializeToXml(myCustomClasses);
Assert.IsNotNull(serialized);
}
}

Get properties from derived class in base class

How do I get properties from derived class in base class?
Base class:
public abstract class BaseModel {
protected static readonly Dictionary<string, Func<BaseModel, object>>
_propertyGetters = typeof(BaseModel).GetProperties().Where(p => _getValidations(p).Length != 0).ToDictionary(p => p.Name, p => _getValueGetter(p));
}
Derived classes:
public class ServerItem : BaseModel, IDataErrorInfo {
[Required(ErrorMessage = "Field name is required.")]
public string Name { get; set; }
}
public class OtherServerItem : BaseModel, IDataErrorInfo {
[Required(ErrorMessage = "Field name is required.")]
public string OtherName { get; set; }
[Required(ErrorMessage = "Field SomethingThatIsOnlyHereis required.")]
public string SomethingThatIsOnlyHere{ get; set; }
}
In this example - can I get the "Name" property from ServerItem class while in BaseModel class?
EDIT:
I'm trying to implement model validation, as described here:
http://weblogs.asp.net/marianor/archive/2009/04/17/wpf-validation-with-attributes-and-idataerrorinfo-interface-in-mvvm.aspx
I figured that if I create some base model with (almost) all of the validation magic in it, and then extend that model, it will be okay...
If both classes are in the same assembly, you can try this:
Assembly
.GetAssembly(typeof(BaseClass))
.GetTypes()
.Where(t => t.IsSubclassOf(typeof(BaseClass))
.SelectMany(t => t.GetProperties());
This will give you all the properties of all the subclasses of BaseClass.
If you require that a derived class must implement a method or property, you should introduce that method or property into the base class as an abstract declaration.
For example, for your Name property, you would add to the base class:
public abstract string Name { get; set; }
Then any derived classes must implement it, or be abstract classes themselves.
Once you have added the abstract version of the Name property to the base class, you will be able to access it in the base class anywhere except in the base class's constructor.
If you must do literally fetch property of derived class from within base class, you can use Reflection, for example - like this...
using System;
public class BaseModel
{
public string getName()
{
return (string) this.GetType().GetProperty("Name").GetValue(this, null);
}
}
public class SubModel : BaseModel
{
public string Name { get; set; }
}
namespace Test
{
class Program
{
static void Main(string[] args)
{
SubModel b = new SubModel();
b.Name = "hello";
System.Console.Out.WriteLine(b.getName()); //prints hello
}
}
}
This is not recommended, though, and you most probably should rethink your design like Matthew said.
As for not throwing properties to your base classes -- you can try to decouple base and deriving classes into unrelated objects and pass them via constructors.
Another way to solve this issue by create virtual property in base class and override it to derived class.
public class Employee
{
public virtual string Name {get; set;}
}
public class GeneralStaff
{
public override string Name {get; set;}
}
class Program
{
static void Main(string[] args)
{
Employee emp = new GeneralStaff();
emp.Name = "Abc Xyz";
//---- More code follows----
}
}
Okay, I solved this problem slightly different than the author of this post: http://weblogs.asp.net/marianor/archive/2009/04/17/wpf-validation-with-attributes-and-idataerrorinfo-interface-in-mvvm.aspx
public abstract class BaseModel : IDataErrorInfo {
protected Type _type;
protected readonly Dictionary<string, ValidationAttribute[]> _validators;
protected readonly Dictionary<string, PropertyInfo> _properties;
public BaseModel() {
_type = this.GetType();
_properties = _type.GetProperties().ToDictionary(p => p.Name, p => p);
_validators = _properties.Where(p => _getValidations(p.Value).Length != 0).ToDictionary(p => p.Value.Name, p => _getValidations(p.Value));
}
protected ValidationAttribute[] _getValidations(PropertyInfo property) {
return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}
public string this[string columnName] {
get {
if (_properties.ContainsKey(columnName)) {
var value = _properties[columnName].GetValue(this, null);
var errors = _validators[columnName].Where(v => !v.IsValid(value)).Select(v => v.ErrorMessage).ToArray();
return string.Join(Environment.NewLine, errors);
}
return string.Empty;
}
}
public string Error {
get { throw new NotImplementedException(); }
}
}
Maybe it will help somebody.
Scan your assembly for all inherited classes from BaseModel and create dictionary like this:
Dictionary<Type, Dictionary<string, Func<BaseModel, object>>>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TESTNEW
{
public abstract class BusinessStructure
{
public BusinessStructure()
{ }
public string Name { get; set; }
public string[] PropertyNames{
get
{
System.Reflection.PropertyInfo[] Pr;
System.Type _type = this.GetType();
Pr = _type.GetProperties();
string[] ReturnValue = new string[Pr.Length];
for (int a = 0; a <= Pr.Length - 1; a++)
{
ReturnValue[a] = Pr[a].Name;
}
return ReturnValue;
}
}
}
public class MyCLS : BusinessStructure
{
public MyCLS() { }
public int ID { get; set; }
public string Value { get; set; }
}
public class Test
{
void Test()
{
MyCLS Cls = new MyCLS();
string[] s = Cls.PropertyNames;
for (int a = 0; a <= s.Length - 1; a++)
{
System.Windows.Forms.MessageBox.Show(s[a].ToString());
}
}
}
}

How to ensure that the serialization and deserialization with WebServices is symmetric?

I have a couple of standard ASP.NET web methods that I'm calling from javascript with a parameter that is of a custom class in form
[DataContract]
[KnownType(typeof(MyOtherSubclass))]
public class MyClass
{
[DataMember]
public MyOtherClass MyMember { get; set; }
}
where MyOtherClass is a class marked with Serializable but not with DataContract attribute (I don't have a control over its generation). There is a couple of subclasses of MyOtherClass, e.g. MyOtherSubclass :
[Serializable]
public class MyOtherSubClass : MyOtherClass
{
private string valueField;
public string Value
{
get { return valueField; }
set { valueField = value; }
}
}
When I use the DataContractJsonSerializer to serialize an object of MyClass directly, I get a result similar to
{ "MyMember" : { "__type" : "MyOtherSubClass:#Namespace", "valueField" : "xxx" } }
However, when I pass such a JSON into the web method request from javascript, I get an exception while deserializing. I have experimented a little bit and found that when using the following one instead
{ "MyMember" : { "___type" : "Namespace.MyOtherSubClass", "Value" : "xxx" } }
the deserialization works without any problems.
Is there any way to configure the DataContractJsonSerializer in such a way that it would produce the JSON in the second form, so that the web method arguments deserialization would work ?
ASP.NET WebMethods use JavaScriptSerializer, so try serializing with it. You might need a custom type resolver in order to include this property:
public class Parent
{
public string ParentProp { get; set; }
}
public class Child: Parent
{
public string ChildProp { get; set; }
}
public class CustomResolver : JavaScriptTypeResolver
{
public override Type ResolveType(string id)
{
return Type.GetType(id);
}
public override string ResolveTypeId(Type type)
{
return type.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var o = new Child
{
ParentProp = "parent prop",
ChildProp = "child prop",
};
var serializer = new JavaScriptSerializer(new CustomResolver());
var s = serializer.Serialize(o);
Console.WriteLine(s);
}
}

Categories