I have a class A like this
class A
{
public string Type { get; set; }
public object[] Content { get; set; }
public string[] jsonContent { get; set; }
public A()
{
}
public A(string type)
{
this.Type = type;
}
public A(string type, object[] content)
{
this.Type = type;
this.Content = content;
}
public string ToJson()
{
int len = Content.Length;
string[] jsonContentTmp = new string[len];
for (int i = 0; i < Content.Length; ++i)
{
jsonContentTmp[i] = JsonConvert.SerializeObject(Content[i]);
}
jsonContent = jsonContentTmp;
var json = JsonConvert.SerializeObject(this);
return json;
}
public static A ToA(string str)
{
Request a = JsonConvert.DeserializeObject<A>(str);
return a;
}
}
Consider bellow:
A sub1 = new A();
A sub2 = new A();
object[] obj = {sub1, sub2};
A test = new A("type", obj);
When I want to serialize test, I receive exception
self reference
I tried PreserveReferencesHandling but i couldn't deserialize and receive exception
'cannot preserve reference toarray'
.
Any idea to serialize and deserialize it?
So it seems this is done on purpose to prevent Stackoverflow exceptions. No pun intended. You have to turn PreserveReferences on, to enable self referencing:
Serialising Circular references
In your AppStart in Global.asax try the following:
var jsonSerializerSettings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonNetFormatter(jsonSerializerSettings));
Related
This is the code that reproduces the problem:
internal class Program
{
private static JsonSerializerSettings Setting = new Newtonsoft.Json.JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
static void Main(string[] args)
{
var script = new SubClass();
script.Param.Add(new Parameter()
{
msg = "I will die"
});
var obj = new Class();
obj.subObj = script;
string json;
string json2;
json = JsonConvert.SerializeObject(obj, Setting);
Console.WriteLine("before:");
Console.WriteLine(json);
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<Class>(json, Setting);
json2 = JsonConvert.SerializeObject(obj, Setting);
Console.WriteLine("after:");
Console.WriteLine(json2);
}
public class Class
{
public IEnumerable<Parameter> parameters => subObj?.parameters();
public SubClass subObj { get; set; }
}
public class Parameter
{
public string msg { get; set; }
}
public class SubClass
{
public List<Parameter> Param { get; set; } = new List<Parameter>();
public IEnumerable<Parameter> parameters()
{
return Param;
}
}
}
The results of this run show that the results of the two serializations are different:
before:
{"$id":"1","parameters":[{"$id":"2","msg":"I will die"}],"subObj":{"$id":"3","Param":[{"$ref":"2"}]}}
after:
{"$id":"1","parameters":[null],"subObj":{"$id":"2","Param":[null]}}
I tried to delete the Class.parameters attribute, and the running results returned to normal. I don't understand why?
i've tried your code and here what I found.
If you delete
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
the code workk just fine.
I think it's a problem of setter because if you put a break point you can see that the problem is cause by the deserialisation.
I know by experience that newtonsoft is tricky with some setter and if they are in private they're unable to be use by it.
I hope that help you
Some strong typed classes:
public class A
{
public C Process(B b)
{
return new C()
{
Output = b.Property + " bar!"
};
}
}
public class B
{
public string Property {get;set;}
}
public class C
{
public string Output {get;set;}
}
Input as JSON string:
string input = "{\"Property\":\"foo\"}";
And what I want is if I have instance of A as object, I want to invoke this JSON and retrieve object output:
object instanceOfA = new A();
object result = instanceOfA.GetType().GetMethod("Process").Invoke(instanceOfA, new []{ /*json here?*/});
So, it is basically similar to some JavaScript call on some plain argument.
What about changing the method signature for the 'Process' method to take a string as input instead and attempting to de-serialize the input to the type you want? So something like this:
string input = "{\"Property\":\"foo\"}";
object instanceOfA = new A();
object result = instanceOfA.GetType().GetMethod("Process").Invoke(instanceOfA, new object[]{ input });
public class A
{
public C Process(string input)
{
var b = (B) JsonConvert.DeserializeObject(input, typeof(B));
if (!string.IsNullOrEmpty(b.Property))
{
return new C()
{
Output = b.Property + " bar!"
};
}
return null;
}
}
public class B
{
public string Property { get; set; }
}
public class C
{
public string Output { get; set; }
}
Problem was simply solved with Newtonsoft.Json features:
public class JsonInvoker
{
private readonly MethodInfo _methodInfo;
private readonly Type _paramType;
private readonly JsonSerializerSettings _settings;
public JsonInvoker(Type instanceType, string methodName)
{
_methodInfo = instanceType.GetMethod(methodName);
_paramType = _methodInfo.GetParameters()[0].ParameterType;
_settings = new JsonSerializerSettings
{
ContractResolver = new RequireObjectPropertiesContractResolver(),
MissingMemberHandling = MissingMemberHandling.Error
};
}
public object Invoke(object instance, string json)
{
var input = JsonConvert.DeserializeObject(json, _paramType, _settings);
var output = _methodInfo.Invoke(instance, new[] { input });
return output;
}
private class RequireObjectPropertiesContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
contract.ItemRequired = Required.AllowNull;
return contract;
}
}
}
Now I can invoke my method:
class Program
{
static void Main(string[] args)
{
object plugin = new A();
string json = "{\"Property\":\"foo\"}";
var invoker = new JsonInvoker(plugin.GetType(), "Process");
//here I call invoker with simple string.
var output = invoker.Invoke(plugin, json);
}
}
public class A
{
public C Process(B input)
{
return new C
{
Property = input.Property + " bar"
};
}
}
public class C
{
public string Property { get; set; }
}
public class B
{
public string Property { get; set; }
}
--- EDIT ---
Brian Rogers said that it does work, and linked to .NET Fiddle (see below). It look in .NET Fiddle as it does call the BindToName, but in my solution it most definately does not:
--- // EDIT ---
When I create a class deriving from SerializationBinder and use that when serializing to or from JSON (using json.net i C#), one method in TypeNameSerializationBinder is never called, and as I can see it, never used at all.
In other words: the serializationbinder method BindToName is never called, and I dont get why
This is the derived class:
public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }
public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = serializedType.FullName; // <-- BREAKPOINT HERE
typeName = ""; // serializedType.Name;
}
public override Type BindToType(string assemblyName, string typeName)
{
string resolvedTypeName = string.Format(TypeFormat, typeName);
Type t = Type.GetType(resolvedTypeName, true); // <-- BREAKPOINT HERE
return t;
}
}
and then a helper class:
public class JSONHandler
{
public static string Serialize<T>(T obj)
{
string json = JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
Binder = new TypeNameSerializationBinder("MyProtocol.{0}")
});
return json;
}
public static T Deserialize<T>(string json)
{
T t = JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
Binder = new TypeNameSerializationBinder("MyProtocol.{0}")
});
return t; }
}
and lastly, this is where i call the helper class to serialize and deserialize:
var resource = new Resource() { ResourceProperty = "Resource" };
var video = new Video() { ResourceProperty = "Video", VideoProperty = "VideoMP4", TapeProperty = new Tape() { SomeOtherValue = "asd", SomeValue = 123 } };
var list = new List<Resource>() { resource, video };
string serialistedList = JSONHandler.Serialize<List<Resource>>(list);
List<Resource> newList = JSONHandler.Deserialize<List<Resource>>(serialistedList);
I need help understanding why the BindToName is never called? BindToType is called on deserialization though... Anyone? =)
---------- DUMMY CLASSES -------------
public class Resource
{
public string ResourceProperty { get; set; }
}
public class Video : Resource
{
public string VideoProperty { get; set; }
public Tape TapeProperty { get; set; }
}
public class Tape
{
public int SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
I'm trying to add some properties to a model arriving from an external assembly (can NOT be modified). So, I get the model, it is serialized as JSON, then deserialized as dynamic object (ExpandoObject), new properties are added, the dynamic object is serialized to JSON; but when I try to deserialize, as everybody should expect the contained type is deserialized with the original type (A class in the example) and not as the extended (ExtendedA in the example). See the following code:
public static class Extensions
{
private static object ConvertJTokenToObject(JToken token)
{
if (token is JValue)
{
return ((JValue)token).Value;
}
if (token is JObject)
{
var expando = new ExpandoObject();
(from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property =>
{
((IDictionary<string, object>)expando).Add(property.Name, ConvertJTokenToObject(property.Value));
});
return expando;
}
if (token is JArray)
{
object[] array = new object[((JArray)token).Count];
int index = 0;
foreach (JToken arrayItem in ((JArray)token))
{
array[index] = ConvertJTokenToObject(arrayItem);
index++;
}
return array;
}
throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
}
public static ExpandoObject ToExpando(this string json)
{
JObject jsonObject = JObject.Parse(json);
dynamic dynObject = ConvertJTokenToObject(jsonObject);
return (ExpandoObject)dynObject;
}
}
public class A //Defined in an external assembly
{
public string Property1 { get; set; }
}
public class B //Defined in an external assembly
{
public List<A> Properties { get; set; }
}
public class ExtendedA : A
{
public string Property2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var myObject = new B() //This is an example but in real code this object arrive from an external module that can't be modified, something like: B myObject = ExternalAssembly.GetB();
{
Properties = new List<A>()
{
new A() { Property1 = "value 1" },
new A() { Property1 = "value 2" },
new A() { Property1 = "value 3" }
}
};
string jsonObject = JsonConvert.SerializeObject(myObject);
dynamic expandoObject = jsonObject.ToExpando();
for (int index = 0; index < expandoObject.Properties.Length; index++)
{
expandoObject.Properties[index].Property2 = Guid.NewGuid();
}
jsonObject = JsonConvert.SerializeObject(expandoObject);
B myExtendedObject = JsonConvert.DeserializeObject<B>(jsonObject);
}
}
How I can deserialize the "new created type" in a way that use class ExtendedA instead class A? There is a way to specify the mapper in the deserialization call? Any suggestion?
I have an object that contains lists of other objects and that looks like this:
public class MyObject {
public int Prop1 { get; set; }
public List<Object1> TheListOfObject1 { get; set; }
public List<Object2> TheListOfObject2 { get; set; }
public string MyObjectInJson { get; set;}
public void MyObjectToJson()
{
JavascriptSerializer TheSerializer = new JavascriptSerializer();
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object1ToJson() });
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object2ToJson() });
MyObjectInJson = TheSerializer.Serialize(this);
}
Now I have another class that's getting a json string of MyObject and I need to deserializse the string and create a MyObject from the string.
I know there's JSON.net and other library available but I want to use .net's JavascriptSerializer.
Suppose that the converters I have also handle the deserializtion. Do I simply do something like this:
1) add FromJson method to MyObject
public this MyObjectFromJson (string MyObjectInJson)
{
JavascriptSerializer TheSerializer = new JavascriptSerializer();
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object1ToJson() });
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object2ToJson() });
this = TheSerializer.DeSerialize(MyObjectInJson);
}
2) and in the calling class write this:
MyObject TheObject = new MyObject();
TheObject = TheObject.MyObjectFromJson(TheIncomingString);
I'm not sure how to proceed. Will this kind of approach work?
Thanks.
Unless your lists of Object1 and Object2 are incredibly complex, you probably don't need the Converters.
Here is a quick sample that I threw together based on your code. Note that the deserializer is static since you will want it to create a new object and you can't set this because it is readonly.
public class MyObject
{
public int Prop1 { get; set; }
public List<Object1> TheListOfObject1 { get; set; }
public List<Object2> TheListOfObject2 { get; set; }
public MyObject()
{
TheListOfObject1 = new List<Object1>();
TheListOfObject2 = new List<Object2>();
}
public string ToJson()
{
JavaScriptSerializer TheSerializer = new JavaScriptSerializer();
return TheSerializer.Serialize(this);
}
public static MyObject FromJson(string sValue)
{
JavaScriptSerializer TheSerializer = new JavaScriptSerializer();
return TheSerializer.Deserialize<MyObject>(sValue);
}
}
This is then called by
MyObject oObject = new MyObject();
oObject.TheListOfObject1.Add(new Object1(1));
oObject.TheListOfObject2.Add(new Object2(2));
oObject.Prop1 = 3;
string sJSON = oObject.ToJson();
System.Diagnostics.Debug.WriteLine(sJSON);
oObject = MyObject.FromJson(sJSON);
System.Diagnostics.Debug.WriteLine(oObject.Prop1);