I am trying to use Newtonsoft Json in my project. What I want to do is like below.
public class Subscribe<T> : BaseSubscribe where T : Message, new() // A class
message.body = JsonConvert.DeserializeObject <T.body>(receiveMessage); // In a class member functiton
I got an error. T is a generic variable so I can not initialize. Is there any ways to use json converter on generic variable?
thanks.
try this:
var message = JsonConvert.DeserializeObject<T>(receiveMessage);
var messageBody = message.body;
But please, share more of your code and the error.
If you want to be able to deserialise generic types using JsonConvert then you will need to help the deserialiser by supplying the concrete type name in your json. This is done using the TypeNameHandling flag on the JsonSerializerSettings object. Example below.
[TestFixture]
public class GenericJsonConvertFixture
{
public abstract class Employee
{
public string Name { get; set; }
}
public class Manager : Employee
{
}
public class Subscribe<T> where T : Employee
{
public T Employee { get; set; }
}
[Test]
public async Task TestDeserialisingGenericTypes()
{
var sub = new Subscribe<Employee>()
{
Employee = new Manager() { Name = "Elon" }
};
var json = JsonConvert.SerializeObject(sub, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
});
var newSub = JsonConvert.DeserializeObject(json, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
});
Assert.That(newSub is Subscribe<Employee>);
Assert.That(((Subscribe<Employee>)newSub).Employee is Manager);
}
}
Related
I'm working on .NET Core where I'm trying to masking some sensitive data like emailid, password, etc.. I want all my json response data keys or properties name should be in lowercase for that I'm using one of the feature of Newtonsoft e.g ContractResolver which helps us to customize property name.
But it seems, it only works with generic class type e.g
class customer
{
public string firstName { get; set; }
}
but it not working when we have json string e.g
{\n \n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n}
now i'm looking for how to convert json response to specific object type dynamically so that i can pass that object to contractResolver checkout below code.
public class LowerCaseResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToLower();
}
}
using Newtonsoft.Json;
using SerializationContractResolverExample.ContractResolver;
using System;
namespace SerializationContractResolverExample
{
class Program
{
static void Main(string[] args)
{
CustomerInfo customerInfo = new CustomerInfo()
{
FirstName = "Sukhpinder",
LastName = "Singh",
MobileNumbers = new System.Collections.Generic.List<string>() {
"33443434343"
}
};
var sestting = new JsonSerializerSettings
{
ContractResolver = new LowerCaseResolver()
};
// need to convert below response to dynamic generic type
string tem1 = "{\n \"lmsUserId\": 10268,\n \"landlordProfileId\": \"2ea81674-6ca6-478c-a9c6-fefbe9572f28\",\n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n \"email\": \"yesteluydi#vusra.com\",\n \"createdBy\": 1551,\n \"createdDate\": \"2022-05-05T17:05:10.37\",\n \"user\": null,\n \"linkedLLCs\": null,\n \"ssn\": null,\n \"accountTypeId\": 2,\n \"completeLater\": false\n}";
var responseLowerCase = JsonConvert.SerializeObject(tem1, Formatting.Indented, sestting); //Not working
var responseLowerCase = JsonConvert.SerializeObject(customerInfo, Formatting.Indented, sestting); //Working Fine
Console.WriteLine(responseLowerCase);
Console.ReadLine();
}
}
}
You can try to use JsonConvert.DeserializeObject to convert tem1 to Object .
string tem1 = "{\n \"lmsUserId\": 10268,\n \"landlordProfileId\": \"2ea81674-6ca6-478c-a9c6-fefbe9572f28\",\n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n \"email\": \"yesteluydi#vusra.com\",\n \"createdBy\": 1551,\n \"createdDate\": \"2022-05-05T17:05:10.37\",\n \"user\": null,\n \"linkedLLCs\": null,\n \"ssn\": null,\n \"accountTypeId\": 2,\n \"completeLater\": false\n}";
var s = JsonConvert.DeserializeObject<Object>(tem1);
result:
This question already has answers here:
Serializing an interface/abstract object using NewtonSoft.JSON
(1 answer)
Why does Json.NET not include $type for the root object when TypeNameHandling is Auto?
(3 answers)
json.net - how to add property $type ONLY on root object
(2 answers)
Closed 4 years ago.
I have encountered an interesting scenario that i don't understand about Json.Net.
I need to serialize objects in order to POST them to API. Objects are created using types with some hierarchy. So in the serialization methods, base type is used to receive the object/objects and serialize it/them using Json.Net with TypeNameHandling.Auto configured.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SerializationTest
{
class Program
{
static void Main(string[] args)
{
var dj = new Dj() { Name = "Tim", Label = "UMG", StageName = "Avicii" };
var djJson = JSerializer.Serialize(dj);
var djs = new List<Person>();
djs.Add(dj);
var djsJson = JSerializer.Serialize(djs);
Console.ReadKey();
}
}
public static class JSerializer
{
public static string Serialize(Person person)
{
var result = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return result;
}
public static string Serialize(IReadOnlyList<Person> people)
{
var eventModelsSerialized = JsonConvert.SerializeObject(people, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return eventModelsSerialized;
}
}
public abstract class Person
{
public string Name { get; set; }
}
public abstract class Artist : Person
{
public string Label { get; set; }
}
public class Dj : Artist
{
public string StageName { get; set; }
}
}
What happens then is that:
for single object serialization case, I got very basic Json without any type information:
{
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
while for list of objects serialization case, we got Json with type information:
[
{
"$type": "SerializationTest.Dj, SerializationTest",
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
]
This creats confusion for server side. So when I want to just serialize single object, the server side has problem to serialize it due to lack of type information. So the workaround is creating a list to include the object and serialize as list instead.
Could anyone explain why Json.Net serialize differently for these two cases? Thanks in advance.
you can wrap your object as given below
var dj = new Dj() { Name = "Tim", Label = "UMG", StageName = "Avicii" };
var obj = new
{
Dj = dj
};
var eventModelsSerialized = JsonConvert.SerializeObject(obj, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
Result with type information:
{
"Dj": {
"StageName": "Avicii",
"Label": "UMG",
"Name": "Tim"
}
}
I have LoginModel:
public class LoginModel : IData
{
public string Email { get; set; }
public string Password { get; set; }
}
and I have the Web api method
public IHttpActionResult Login([FromBody] LoginModel model)
{
return this.Ok(model);
}
And it's return 200 and body:
{
Email: "dfdf",
Password: "dsfsdf"
}
But I want to get with lower first letter in property like
{
email: "dfdf",
password: "dsfsdf"
}
And I have Json contract resolver for correcting
public class FirstLowerContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
if (string.IsNullOrWhiteSpace(propertyName))
return string.Empty;
return $"{char.ToLower(propertyName[0])}{propertyName.Substring(1)}";
}
}
How I can apply this?
If your are using Newtonsoft.Json, you can add JsonProperties to your view model :
public class LoginModel : IData
{
[JsonProperty(PropertyName = "email")]
public string Email {get;set;}
[JsonProperty(PropertyName = "password")]
public string Password {get;set;}
}
To force all json data returned from api to camel case it's easier to use Newtonsoft Json with the default camel case contract resolver.
Create a class like this one:
using Newtonsoft.Json.Serialization;
internal class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
_jsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
return new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
}
}
and set this during api configuration (at startup):
var jsonFormatter = new JsonMediaTypeFormatter();
httpConfiguration.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
You can add the two following statement in the configuration of the web API or to the startup file
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
But it is very important to use the return Ok() method instead of return Json() otherwise; this will not work.
if you have to use Json method (and have no other choice)
then see this answer https://stackoverflow.com/a/28960505/4390133
If you need it only in some certain place and not throughout whole application, then you can do following:
var objectToSerialize = new {Property1 = "value1", SubOjbect = new { SubObjectId = 1 }};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(data, new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() });
It should result in {"property1":"value1","subOjbect":{"subObjectId":1}} (note that nested properties also starts from lowercase)
I have a call to a WebAPI with the following code:
var client = new HttpClient
{
BaseAddress = new Uri("http://localhost:8490/")
};
var jObject = new JObject();
jObject.Add("paramA", paramA);
jObject.Add("paramB", paramB);
JArray jArr = JArray.FromObject(paramsGenericArr);
jObject.Add("paramC", jArr);
var content = new StringContent(jObject.ToString(), Encoding.UTF8, "application/json");
var result = await client.PostAsync("api/path/tofunc", content).ConfigureAwait(false);
result.EnsureSuccessStatusCode();
The ParamsGeneric class is an abstract type with 2 derived classes:
[DataContract]
public class ParamsTypeA : ParamsGeneric
{
[DataMember]
public long itemC {get; set;}
public ParamsTypeA() :
base()
{}
}
[DataContract]
public class ParamsTypeB : ParamsGeneric
{
[DataMember]
public long itemD {get; set;}
public ParamsTypeB() :
base()
{}
}
[DataContract]
[KnownType(typeof(ParamsTypeA))]
[KnownType(typeof(ParamsTypeB))]
public abstract class ParamsGeneric
{
[DataMember]
public long itemA { get; set; }
[DataMember]
public long itemB {get; set;}
public ParamsGeneric()
{}
}
I suspect that I have a problem with the deserialization in the WebAPI:
public class ClientData
{
public string paramA { get; set; }
public string paramB { get; set; }
public ParamsGeneric[] paramC { get; set; }
}
[HttpPost]
[Route("api/path/tofunc")]
public async Task<bool> DoStuffAsync(ClientData clientData)
{
....
}
I have a problem with the paramsGenericArr/paramC (which is of type ParamsGeneric[], and holds items of type ParamsTypeA & ParamsTypeB)
The WebAPI receives a blank array (ParamsGeneric[0]), along with the other parameters.
Help will be appriciated.
UPDATE
Even if I try to pass a single ParamsGeneric object instead of an array, I receive null instead of the object.
SOLUTION
var serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Auto;
JArray jArr = JArray.FromObject(paramsGenericArr, serializer);
Did the trick.
While inheritance in messages / json is definitely possible, IMHO; it's just too much of a hassle :)
Anyway, you can actually let Newtonsoft.Json handle the inheritance for you, by setting TypeNameHandling
// Allow inheritance in json payload
JsonSerializerSettings serializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
serializerSettings.TypeNameHandling = TypeNameHandling.All;
.. or just
serializerSettings.TypeNameHandling = TypeNameHandling.Auto;
.. depending on your needs.
This is the easy fix, which will work fine if it's an internal API or if you can guarantee you will always be in control of the clients. If you have external clients, I would go the 'override default model binder'-approach such as what is posted here "Deserialising Json to derived types in Asp.Net Web API" - or very strongly consider avoiding inheritance in the model of the API.
Try to pass data to your API as below
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("paramA", paramA);
param.Add("paramB", paramB);
HttpClient client = new HttpClient();
HttpFormUrlEncodedContent contents = new HttpFormUrlEncodedContent(param);
var result = await client.PostAsync(new Uri("http://localhost:8490/api/path/tofunc")
, contents);
var reply = await result.Content.ReadAsStringAsync();
if (reply.IsSuccessStatusCode)
{
}
Hope this will help you.
[HttpPost]
[Route("api/path/tofunc")]
public async Task<bool> DoStuffAsync([FromBody]ClientData clientData)
{
....
}
Please keep [FromBody] in the web api method, so that model binder will map your body data to parameter i.e clientData.
I am sending 3 .net objects over the network:
- List<int>
- List<ParentObject>
- string
This is how I'm serializing (same for all types):
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Arrays
};
string message = JsonConvert.SerializeObject(listOfParents, JSsettings);
//listOfParents is of type List<ParentObject>
The ParentObject is an abstract class and has two child classes. It has a property to get which child type it represents.
public enum EntityType {Child1, Child2};
class ParentObject
{
public EntityType et { get; set; }
//..other members
}
I want to call 3 different functions based on which of the 3 objects is received.
Object genericObject = JsonConvert.DeserializeObject(message, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
if (genericObject is List<int>)
{
List<int> myList= (List<int>)genericObject;
myfunction1(myList);
}
if (genericObject is List<ParentObject>)
{
//etc..
The ParentObject is causing problems at DeserializeObject() because it says "Could not create an instance of type ParentObject. Type is an interface or abstract class and cannot be instantiated". So I was thinking I might need to use CustomCreationConverter at http://james.newtonking.com/projects/json/help/index.html?topic=html/CustomCreationConverter.htm
That still wouldn't solve my problem since the CustomCreationConverter needs the type during deserialization whereas I don't check the type until after deserialization.
Any suggestions to solve the problem?
If I use objects defined like this:
public enum EntityType { Child1, Child2 };
abstract class ParentObject
{
public EntityType et { get; set; }
}
class ChildClass : ParentObject
{
public int ChildClassProp { get; set; }
public ChildClass()
{
this.et = EntityType.Child1;
}
}
class ChildClass2 : ParentObject
{
public int ChildClass2Prop { get; set; }
public ChildClass2()
{
this.et = EntityType.Child2;
}
}
Then I can happily deserialize derived classes(ChildClass and ChildClass2) to ParentObject like this:
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });
string message = JsonConvert.SerializeObject(list,
Newtonsoft.Json.Formatting.Indented, JSsettings);
list = JsonConvert.DeserializeObject<List<ParentObject>>(message, JSsettings);
Where message looks like this:
[
{
"$type": "ConsoleApplication4.ChildClass, ConsoleApplication4",
"ChildClassProp": 1,
"et": 0
},
{
"$type": "ConsoleApplication4.ChildClass2, ConsoleApplication4",
"ChildClass2Prop": 2,
"et": 1
}
]
The key in this one was using TypeNameHandling = TypeNameHandling.Auto for both serialization and deserialization. Using TypeNameHandling.Arrays creates a message that looks like this:
{
"$type": "System.Collections.Generic.List`1[[ConsoleApplication4.ParentObject, ConsoleApplication4]], mscorlib",
"$values": [
{
"ChildClassProp": 1,
"et": 0
},
{
"ChildClass2Prop": 2,
"et": 1
}
]
}
Note that the types of the list items are not included, only the type of the list, hence the error you were getting.
Edit:
I think the easiest way to get this working the way you want is to define a simple class like this one that acts as a thin wrapper around the object you are serializing:
class ObjectContainer
{
public object Data { get; set; }
}
Then the code would look like this (note the change to TypeNameHandling.Auto):
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
};
List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });
ObjectContainer container = new ObjectContainer()
{
Data = list
};
string message = JsonConvert.SerializeObject(container,
Newtonsoft.Json.Formatting.Indented, JSsettings);
var objectContainer = JsonConvert.DeserializeObject<ObjectContainer>(message, JSsettings);
if (objectContainer.Data is List<int>)
{
Console.Write("objectContainer.Data is List<int>");
}
else if (objectContainer.Data is List<ParentObject>)
{
Console.Write("objectContainer.Data is List<ParentObject>");
}
else if (objectContainer.Data is string)
{
Console.Write("objectContainer.Data is string");
}
The reason I have gone for this approach is that Json.Net will take care of almost all the work. Simply calling the non-generic JsonConvert.DeserializeObject method is fine, but you would then need to do additional work as this method returns a JContainer, not an object.