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
Related
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; }
}
I'm working on a .NET application for my company to interact with the Nutshell CRM. They have documentation provided for their JSON API here. I'm slowly building out all the classes in my application, but I've run into the issue of only needing to update one field on a call, but having the application include every field that I have on that class.
So for a condensed example of the editLead method, where I'm only modifying the customFields:
Nutshell documentation states that all fields are optional. My classes are set up as the following, where my custom fields in Nutshell are Division, Product, Country:
public class editLead
{
public Customfields customFields { get; set; }
}
public class Customfields
{
public string Division { get; set; }
public string Product { get; set; }
public string Country { get; set; }
}
EDIT (adding more code):
[DataContract(Name = "params")]
public class EditLeadParams
{
public string leadId { get; set; }
public editLead lead { get; set; }
public string rev { get; set; }
}
I'm using RestSharp to make the following call:
var editleadclient = new RestClient();
Method editleadMethod = new Method();
editleadMethod = Method.POST;
var editleadrequest = new RestRequest(editleadMethod);
editleadrequest.RequestFormat = DataFormat.Json;
editleadclient.BaseUrl = new Uri(apiuri);
editleadrequest.Credentials = new NetworkCredential(login, apikey);
leadJSON.EditLeadParams lead1 = new leadJSON.EditLeadParams()
{
leadId = foundlead[0],
lead = new leadJSON.editLead()
{
customFields = new leadJSON.Customfields()
{
Division = "AMERICAS",
}
},
rev = foundlead[1],
};
leadJSON.EditLeadRequest editreq = new leadJSON.EditLeadRequest()
{
#params = lead1,
method = "editLead",
};
editleadrequest.AddBody(editreq);
IRestResponse editResponse = editleadclient.Execute(editleadrequest);
If I only want to update the Division, it will use the following JSON {"customFields":{"Division":"AMERICAS","Product":null,"Country":null}}, and overwrite the Product and Country fields and make them blank. However, if I comment out the Product and Country, in the Customfields definition, it will update the Division and leave the Product and Country alone.
Is there another way to define these classes so that I can have it all defined, but only update what needs to be?
Declaration:
//JsonSerializer.cs
public static class JsonSerializer
{
public static string Serialize(object target, bool ignoreNulls = true)
{
var javaScriptSerializer = new JavaScriptSerializer();
if (ignoreNulls)
{
javaScriptSerializer.RegisterConverters(new[]
{
new NullExclusionConverter(target)
});
}
return javaScriptSerializer.Serialize(target);
}
}
//NullExclusionConverter.cs
public class NullExclusionConverter : JavaScriptConverter
{
private readonly Type _type;
public NullExclusionConverter(object target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
this._type = target.GetType();
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new[] { this._type };
}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
if (obj == null)
{
return result;
}
var properties = obj.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
//Use propertyInfo.Name to exclude a specific property name
if (propertyInfo.GetValue(obj, null) == null)
{
continue;
}
result.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null));
}
return result;
}
}
Usage:
string jsonString = JsonSerializer.Serialize(objectToSerialize);
Add a reference to System.Web.Extensions
I am leaving my initial answer because it does return only non-null properties as a Json string. However here is your answer when using RestSharp.
On your request add:
editleadrequest.JsonSerializer.Options = new SerializerOptions()
{
SkipNullProperties = true
};
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));
--- 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 doing a project where I dynamically build a set of objects based on reflection and serialize it (using json.net).
What I want to do is to deserialize it and recreate the object tree, but I don't know the final class type.
So how can I find out?
Here's an example:
public class insideBleah
{
public int BProperty
{ get; set; }
}
public class bleah
{
public int AProperty
{ get; set; }
public insideBleah Inside
{ get; set; }
}
and
var bleah = new bleah();
bleah.AProperty = 1;
bleah.Inside = new insideBleah();
bleah.Inside.BProperty = 2;
var output = JsonConvert.SerializeObject(bleah, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
});
This produces the output string:
"{\"$type\":\"jsontest.bleah, jsontest\",\"AProperty\":1,\"Inside\":{\"$type\":\"jsontest.insideBleah, jsontest\",\"BProperty\":2}}"
So I can see the class in there. It's so close! But I can't seem to tease the information out.
How can I do this? I can't do:
var newObject = (bleah)JsonConvert.DeserializeObject<bleah>(output);
because I don't know it's a 'bleah'.
How can I handle this?
Thanks in advance
Why can you just use:
using System;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
class Program
{
private static void Main(string[] args)
{
var bleah = new Person();
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects };
var output = JsonConvert.SerializeObject(bleah, settings);
Console.WriteLine(output);
var deserializeObject = JsonConvert.DeserializeObject(output, settings);
Console.WriteLine(deserializeObject.GetType().Name);
}
}
class Person
{
public string Name { get; set; }
}
}
The output:
{"$type":"ConsoleApplication1.Person, ConsoleApplication1","Name":null}
Person