Can we initialize JSON object with a string in C# ;
like: "Person": [{"age":"42","name":"John"}]
as object JsonData = "Person": [{"age":"42","name":"John"}];
???
So that i can give this JSON object directly to the DatacontractJSONSerializer
And i could get the data out of it.!
List<Person> people = new List<Person>{
new Person{age = 1, name = "Scott"},
new Person{age = 2, name = "Bill"}
};
string jsonString = ExtensionMethods.JSONHelper.ToJSON(people);
}
}
}
namespace ExtensionMethods
{
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
}
So,
string jsonString = ExtensionMethods.JSONHelper.ToJSON(people);
Gives a string of : [{},{}]
Empty data structure, Any idea..?
With extension methods, you want to patch your method onto the type that you intend to call that method against. In this case, IEnumerable is a good place to add methods you want to use on a List:
public class Person {
public int age { get; set; }
public string name { get; set; }
}
public static class JSONHelper {
public static string ToJSON(this IEnumerable obj) {
return new JavaScriptSerializer().Serialize(obj);
}
}
void Main() {
List<Person> people = new List<Person> {
new Person() { age = 1, name = "Scott" },
new Person() { age = 2, name = "Bill" }
};
// [{"age":1,"name":"Scott"},{"age":2,"name":"Bill"}]
string json = people.ToJSON();
}
The important distinction is that you should use the extension method against a variable of the type it's defined against. You shouldn't reference the extension method directly.
Related
We have an interface and a base class with multiple derived types.
public interface IEvent
{
[JsonProperty("id")]
public string Id { get; set; }
string Type { get; }
}
public abstract class EventBase: IEvent
{
public string Id { get; set; }
public abstract string Type { get; }
}
public class UserCreated : EventBase
{
public override string Type { get; } = typeof(UserCreated).AssemblyQualifiedName;
}
public class UserUpdated : EventBase
{
public override string Type { get; } = typeof(UserUpdated).AssemblyQualifiedName;
}
We are storing these events of different derived types in the same container in Cosmos DB using v3 of .Net SDK Microsoft.Azure.Cosmos. We then want to read all the events and have them deserialized to the correct type.
public class CosmosDbTests
{
[Fact]
public async Task TestFetchingDerivedTypes()
{
var endpoint = "";
var authKey = "";
var databaseId ="";
var containerId="";
var client = new CosmosClient(endpoint, authKey);
var container = client.GetContainer(databaseId, containerId);
await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });
var queryable = container.GetItemLinqQueryable<IEvent>();
var query = queryable.ToFeedIterator();
var list = new List<IEvent>();
while (query.HasMoreResults)
{
list.AddRange(await query.ReadNextAsync());
}
Assert.NotEmpty(list);
}
}
Doesn't seem to be any option to tell GetItemLinqQueryable how to handle types. Is there any other method or approach to support multiple derived types in one query?
It's ok to put the events in some kind of wrapper entity if that would help, but they aren't allowed to be stored as an serialized sting inside a property.
The comment from Stephen Clearly pointed me in the right direction and with the help of this blog https://thomaslevesque.com/2019/10/15/handling-type-hierarchies-in-cosmos-db-part-2/ I ended up with a solution similar to the following example were we have a custom CosmosSerializer that uses a custom JsonConverter that reads the Type property.
public interface IEvent
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("$type")]
string Type { get; }
}
public abstract class EventBase: IEvent
{
public string Id { get; set; }
public string Type => GetType().AssemblyQualifiedName;
}
public class UserCreated : EventBase
{
}
public class UserUpdated : EventBase
{
}
EventJsonConverter reads the Type property.
public class EventJsonConverter : JsonConverter
{
// This converter handles only deserialization, not serialization.
public override bool CanRead => true;
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
// Only if the target type is the abstract base class
return objectType == typeof(IEvent);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// First, just read the JSON as a JObject
var obj = JObject.Load(reader);
// Then look at the $type property:
var typeName = obj["$type"]?.Value<string>();
return typeName == null ? null : obj.ToObject(Type.GetType(typeName), serializer);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("This converter handles only deserialization, not serialization.");
}
}
The NewtonsoftJsonCosmosSerializer takes a JsonSerializerSettings that it uses for serialization.
public class NewtonsoftJsonCosmosSerializer : CosmosSerializer
{
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
private readonly JsonSerializer _serializer;
public NewtonsoftJsonCosmosSerializer(JsonSerializerSettings settings)
{
_serializer = JsonSerializer.Create(settings);
}
public override T FromStream<T>(Stream stream)
{
if (typeof(Stream).IsAssignableFrom(typeof(T)))
{
return (T)(object)stream;
}
using var sr = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(sr);
return _serializer.Deserialize<T>(jsonTextReader);
}
public override Stream ToStream<T>(T input)
{
var streamPayload = new MemoryStream();
using var streamWriter = new StreamWriter(streamPayload, encoding: DefaultEncoding, bufferSize: 1024, leaveOpen: true);
using JsonWriter writer = new JsonTextWriter(streamWriter);
writer.Formatting = _serializer.Formatting;
_serializer.Serialize(writer, input);
writer.Flush();
streamWriter.Flush();
streamPayload.Position = 0;
return streamPayload;
}
}
The CosmosClient is now created with our own NewtonsoftJsonCosmosSerializer and EventJsonConverter.
public class CosmosDbTests
{
[Fact]
public async Task TestFetchingDerivedTypes()
{
var endpoint = "";
var authKey = "";
var databaseId ="";
var containerId="";
var client = new CosmosClient(endpoint, authKey, new CosmosClientOptions
{
Serializer = new NewtonsoftJsonCosmosSerializer(new JsonSerializerSettings
{
Converters = { new EventJsonConverter() }
})
});
var container = client.GetContainer(databaseId, containerId);
await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });
var queryable = container.GetItemLinqQueryable<IEvent>();
var query = queryable.ToFeedIterator();
var list = new List<IEvent>();
while (query.HasMoreResults)
{
list.AddRange(await query.ReadNextAsync());
}
Assert.NotEmpty(list);
}
}
I want to modify my json.NET serializer to add the $type property only to the objects which implements a given interface but not to any property or nested objects.
With TypeNameHandling.Auto (default)
{
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": [1, 2, 3, 4]
}
With TypeNameHandling.All
{
"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": {
"$type": "System.Collections.Generic.List`1[[System.Int32, mscorlib]], mscorlib",
"$values": [1, 2, 3, 4 ]
}
}
What I want
{
"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": [1, 2, 3, 4]
}
I am experimenting with a custom ContractResolver but I don't get it to work:
class Program
{
static void Main(string[] args)
{
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new EnableTypeNameHandlingAllOnlyForEvents(),
Formatting = Formatting.Indented
};
var event1 = new TestEvent() { PropertyA = 123, PropertyB = "foo", PropertyC = new List<int> { 1, 2, 3, 4 } };
string event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
Console.WriteLine(event1Serialized);
Console.ReadLine();
}
}
public interface IEvent
{
}
public class TestEvent : IEvent
{
public int PropertyA { get; set; }
public string PropertyB { get; set; }
public List<int> PropertyC { get; set; }
}
public class EnableTypeNameHandlingAllOnlyForEvents : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var x = base.CreateObjectContract(objectType);
if (typeof(IEvent).IsAssignableFrom(x.UnderlyingType))
{
// What to do to tell json.NET to add $type to instances of this (IEvent) type???
}
return x;
}
}
If you require the "$type" property on your root object and are OK with it appearing on nested polymorphic objects and arrays if required, use the following overload along with TypeNameHandling.Auto: JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings).
From the docs:
public static string SerializeObject(
Object value,
Type type,
JsonSerializerSettings settings
)
type
Type: System.Type
The type of the value being serialized. This parameter is used when TypeNameHandling is Auto to write out the type name if the type of the value does not match. Specifing the type is optional.
I.e., do:
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, typeof(IEvent), serializerSettings);
If you require "$type" on the root object and will not accept it on nested polymorphic objects and arrays even if otherwise required, you will need to use TypeNameHandling.All along with a custom contract resolver that sets JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None:
public class SuppressItemTypeNameContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
var containerContract = contract as JsonContainerContract;
if (containerContract != null)
{
if (containerContract.ItemTypeNameHandling == null)
containerContract.ItemTypeNameHandling = TypeNameHandling.None;
}
return contract;
}
}
Then use it like:
static IContractResolver suppressItemTypeNameContractResolver = new SuppressItemTypeNameContractResolver();
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ContractResolver = suppressItemTypeNameContractResolver,
// Other settings as required.
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
Notes:
Be aware of this caution from the Newtonsoft docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
You may want to statically cache the contract resolver for best performance.
Simpler solution is to override the TypeHandling on property level
public class TestEvent : IEvent
{
public int PropertyA { get; set; }
public string PropertyB { get; set; }
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public List<int> PropertyC { get; set; }
}
I write this code for read json in httphandler:
var jsonSerilizer = new JavaScriptSerializer();
var jsonString = String.Empty;
context.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(context.Request.InputStream))
{
jsonString = inputStream.ReadToEnd();
}
and my json string is:
{"r_id":"140","name":"d","count":"5","c_id":"150"}
and i use this method for parse json string:
JavaScriptSerializer j = new JavaScriptSerializer();
dynamic a = j.Deserialize(jsonString, typeof(object));
string r_id = a["r_id"];
string Name = a["name"];
string count = a["count"];
string c_id = a["c_id"];
up code parse my json string to :
r_id:140
name:d
count:5
c_id:50
When client send me array of string json for example :
{"r_id":"140","name":"d","count":"5","c_id":"150"}
{"r_id":"150","name":"der","count":"50","c_id":"150"}
i can parse up json string
How can i?
I use this code:
var jsonSerilizer = new JavaScriptSerializer();
var jsonString = String.Empty;
context.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(context.Request.InputStream))
{
jsonString = inputStream.ReadToEnd();
}
File.AppendAllText(#"d:\status\LOL.txt", "GetJSON to FROM:"+ jsonString+"\r\n", Encoding.UTF8);
JavaScriptSerializer j = new JavaScriptSerializer();
dynamic a = j.Deserialize(jsonString, typeof(List<ClientMessage>));
foreach (var obj in a)
{
File.AppendAllText(#"d:\status\LOL.txt", obj.name + "\r\n", Encoding.UTF8);
}
but when program recieve to File.AppendAll.. program crash and down.
First of all try to create a model ( class to hold your objects) like this :
Class ClientMessage {
public string r_id {get;set;}
public string name {get;set;}
public string count {get;set;}
public string c_id {get;set;}
}
In this case you would receive a List , so try to do it like this :
JavaScriptSerializer j = new JavaScriptSerializer();
dynamic a = j.Deserialize(jsonString, typeof(List<ClientMessage>));
// and then iterate on your object
foreach (var obj in a)
{
//start assigning values
}
I would suggest creating an object you can parse your JSON into, so something like:
public MyClass {
public int r_id { get; set;}
public string name { get; set; }
// etc
}
This will allow you to parse directly into that object, like this:
var results = j.Deserialize<List<MyClass>>(jsonString);
Can you try something this code, I am assuming you are getting json data like below.
[
{
r_id: "123",
name: "deepu",
count:"5",
c_id:"150"
},
{
r_id: "444",
name: "aaa",
count:"25",
c_id:"55"
},
{
r_id: "5467",
name: "dfgdf",
count:"5",
c_id:"3434"
}
]
I am using the above Client Message class here and adding JsonProperty attribute.
public class ClientMessage
{
[JsonProperty("r_id")]
public string RId { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("count")]
public string Count { get; set; }
[JsonProperty("c_id")]
public string Cid { get; set; }
}
Finally, get the json data fort testing, I am reading the data from external file..
var data = System.IO.File.ReadAllText("jsondata.txt");
var jsResult = JsonConvert.DeserializeObject<List<ClientMessage>>(data);
NOTE : You need to add Newtonsoft.Json reference in your project
I am having an XML string like
<?xml version="1.0"?>
<FullServiceAddressCorrectionDelivery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AuthenticationInfo xmlns="http://www.usps.com/postalone/services/UserAuthenticationSchema">
<UserId xmlns="">FAPushService</UserId>
<UserPassword xmlns="">Password4Now</UserPassword>
</AuthenticationInfo>
</FullServiceAddressCorrectionDelivery>
In Order to map the nodes with Class, i am having the class structure in WCF like
[DataContract]
[Serializable]
public class FullServiceAddressCorrectionDelivery
{
[XmlElement("AuthenticationInfo", Namespace = "http://www.usps.com/postalone/services/UserAuthenticationSchema")]
[DataMember]
public AuthenticationInfo AuthenticationInfo { get; set; }
}
[DataContract]
[Serializable]
public class AuthenticationInfo
{
[DataMember]
[XmlElement("UserId", Namespace = "")]
public string UserId { get; set; }
[DataMember]
[XmlElement("UserPassword", Namespace = "")]
public string UserPassword { get; set; }
}
For De-serialization , i used xmlserializer to De-serialize the object
XmlSerializer xs = new XmlSerializer(typeof(FullServiceAddressCorrectionDelivery));
var result = (FullServiceAddressCorrectionDelivery)xs.Deserialize(stream);
this method returned me a Null FullServiceAddressCorrectionDelivery object..
but when i used DataContractSerializer .. like
DataContractSerializer xs = new DataContractSerializer(typeof(FullServiceAddressCorrectionDelivery));
var result = (FullServiceAddressCorrectionDelivery)xs.ReadObject(stream);
then following exception came out..
Error in line 2 position 46. Expecting element 'FullServiceAddressCorrectionDelivery' from namespace 'http://schemas.datacontract.org/2004/07/WcfService1'.. Encountered 'Element' with name 'FullServiceAddressCorrectionDelivery', namespace ''.
i am stuck with this...
i somehow with the help of RENE(StackOverFlow member) succeded to deserialize if the class was in same project .. but when i converted them to WCF Datacontracts.. i came across that issue ..... please guide me where i am doing wrong here...
Depending on how you want to use the data contract serializer (DCS) for that input, you may or may not be able to do that. The namespace of the data members in the DCS are defined by the namespace of the contract to which they belong, unless it's the root element (in which case the [DC] namespace also defines the namespace of the element).
Your root element, FullServiceAddressCorrectionDelivery, has one namespace in your sample XML (empty), and its member, AuthenticationInfo, has another (http://www.usps.com/postalone...). That means that unless you actually change how the serializer is created, you won't be able to use the DCS for this type.
The XmlSerializer (XS), however, should work just fine - members of the type can have different namespaces. As you can see in the code below, I can post the XML you provided verbatim to an operation which takes the FullServiceAddressCorrectionDelivery as an input, and the object is properly populated - you need to mark the operation (or the contract) with the [XmlSerializerFormat] attribute to get this behavior.
public class Post_6fc3a1bd_b3ca_48da_b4d2_35271135ed8a
{
const string XML = #"<?xml version=""1.0""?>
<FullServiceAddressCorrectionDelivery xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<AuthenticationInfo xmlns=""http://www.usps.com/postalone/services/UserAuthenticationSchema"">
<UserId xmlns="""">FAPushService</UserId>
<UserPassword xmlns="""">Password4Now</UserPassword>
</AuthenticationInfo>
</FullServiceAddressCorrectionDelivery>";
[XmlRoot(ElementName = "FullServiceAddressCorrectionDelivery", Namespace = "")]
public class FullServiceAddressCorrectionDelivery
{
[XmlElement("AuthenticationInfo", Namespace = "http://www.usps.com/postalone/services/UserAuthenticationSchema")]
[DataMember]
public AuthenticationInfo AuthenticationInfo { get; set; }
}
public class AuthenticationInfo
{
[XmlElement("UserId", Namespace = "")]
public string UserId { get; set; }
[XmlElement("UserPassword", Namespace = "")]
public string UserPassword { get; set; }
}
[ServiceContract(Namespace = "")]
public interface ITest
{
[XmlSerializerFormat]
[OperationContract]
FullServiceAddressCorrectionDelivery Echo(FullServiceAddressCorrectionDelivery input);
}
public class Service : ITest
{
public FullServiceAddressCorrectionDelivery Echo(FullServiceAddressCorrectionDelivery input)
{
return input;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
c.Headers[HttpRequestHeader.ContentType] = "text/xml";
Console.WriteLine(c.UploadString(baseAddress + "/Echo", XML));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
For completeness sake, this is how you'd change the serializer creation (passing the root name and namespace) to be able to deserialize the XML you provided using the data contract serializer.
public class Post_6fc3a1bd_b3ca_48da_b4d2_35271135ed8a_b
{
const string XML = #"<?xml version=""1.0""?>
<FullServiceAddressCorrectionDelivery xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<AuthenticationInfo xmlns=""http://www.usps.com/postalone/services/UserAuthenticationSchema"">
<UserId xmlns="""">FAPushService</UserId>
<UserPassword xmlns="""">Password4Now</UserPassword>
</AuthenticationInfo>
</FullServiceAddressCorrectionDelivery>";
[DataContract(Name = "FullServiceAddressCorrectionDelivery", Namespace = "http://www.usps.com/postalone/services/UserAuthenticationSchema")]
public class FullServiceAddressCorrectionDelivery
{
[DataMember]
public AuthenticationInfo AuthenticationInfo { get; set; }
}
[DataContract(Name = "AuthenticationInfo", Namespace = "")]
public class AuthenticationInfo
{
[DataMember]
public string UserId { get; set; }
[DataMember]
public string UserPassword { get; set; }
}
static string Serialize(object obj, bool useDataContractSerializer)
{
MemoryStream ms = new MemoryStream();
XmlWriterSettings ws = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
OmitXmlDeclaration = false,
Encoding = new UTF8Encoding(false)
};
XmlWriter w = XmlWriter.Create(ms, ws);
if (useDataContractSerializer)
{
var dcs = new DataContractSerializer(obj.GetType(), "FullServiceAddressCorrectionDelivery", "");
dcs.WriteObject(w, obj);
}
else
{
new XmlSerializer(obj.GetType()).Serialize(w, obj);
}
w.Flush();
string result = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
w.Close();
ms.Close();
return result;
}
public static void Test()
{
Console.WriteLine("Serialization:");
MemoryStream ms = new MemoryStream();
XmlWriterSettings ws = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
OmitXmlDeclaration = false,
Encoding = new UTF8Encoding(false)
};
XmlWriter w = XmlWriter.Create(ms, ws);
var dcs = new DataContractSerializer(typeof(FullServiceAddressCorrectionDelivery), "FullServiceAddressCorrectionDelivery", "");
var obj = new FullServiceAddressCorrectionDelivery
{
AuthenticationInfo = new AuthenticationInfo
{
UserId = "FAPushService",
UserPassword = "Password4Now"
}
};
dcs.WriteObject(w, obj);
w.Flush();
string result = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
Console.WriteLine();
Console.WriteLine("Deserialization:");
ms = new MemoryStream(Encoding.UTF8.GetBytes(XML));
var obj2 = (FullServiceAddressCorrectionDelivery)dcs.ReadObject(ms);
Console.WriteLine("{0} - {1}", obj2.AuthenticationInfo.UserId, obj2.AuthenticationInfo.UserPassword);
}
}
I have a method doing this:
public JsonResult Layar(string countryCode, string timestamp, string userId,
string developerId, string layarName, double radius,
double lat, double lon, double accuracy)
{
LayarModel model = new LayarModel(lat, lon, radius);
return Json(model, JsonRequestBehavior.AllowGet);
}
It returns this object:
public class LayarModel
{
private List<HotSpot> _hotSpots = new List<HotSpot>();
public List<HotSpot> HotSpots { get { return _hotSpots; } set { _hotSpots = value; } }
public string Name { get; set; }
public int ErrorCode { get; set; }
public string ErrorString { get; set; }
}
I want the JSON to be
{"hotspots": [{
"distance": 100,
"attribution": "The Location of the Layar Office",
"title": "The Layar Office",
"lon": 4884339,
"imageURL": http:\/\/custom.layar.nl\/layarimage.jpeg,
"line4": "1019DW Amsterdam",
"line3": "distance:%distance%",
"line2": "Rietlandpark 301",
"actions": [],
"lat": 52374544,
"type": 0,
"id": "test_1"}],
"layer": "snowy4",
"errorString": "ok",
"morePages": false,
"errorCode": 0,
"nextPageKey": null
}
Everything comes out capitalised as in the class being returned (HotSpots instead of hotspots).
I have tried DataContract and DataMembers(Name="Test") but that doesn't work. Any suggestions?
JsonResult() uses JavaScriptSerializer internally for serialization, and it seems it doesn't support defining the serialized property names using attributes.
DataContractJsonSerializer supports this, so that may be a way to go.
Some links that may be useful:
JavaScriptSerializer.Deserialize - how to change field names : JavaScriptSerializer.Deserialize - how to change field names
DataContractJsonSerializer Versus JavaScriptSerializer : Changing Field Names: http://publicityson.blogspot.com/2010/06/datacontractjsonserializer-versus.html
I would also recommend installing json.NET but the rest is a lot easier. Below is an extension method I am using in my current application to provide better reuse, feel free to adapt it to your needs, but it should do what you need right out of the box.
public class JsonNetResult : ActionResult
{
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings
{
//http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx
#if DEBUG
Formatting = Formatting.Indented, //Makes the outputted Json easier reading by a human, only needed in debug
#endif
ContractResolver = new CamelCasePropertyNamesContractResolver() //Makes the default for properties outputted by Json to use camelCaps
};
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
JsonTextWriter writer = new JsonTextWriter(response.Output) {Formatting = Formatting};
JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
}
public static class JsonNetExtenionMethods
{
public static ActionResult JsonNet(this Controller controller, object data)
{
return new JsonNetResult() {Data = data};
}
public static ActionResult JsonNet(this Controller controller, object data, string contentType)
{
return new JsonNetResult() { Data = data, ContentType = contentType };
}
public static ActionResult JsonNet(this Controller controller, object data, Formatting formatting)
{
return new JsonNetResult() {Data = data, Formatting = formatting};
}
}
Here's an example of using it.
public JsonNetResult Layar(string countryCode, string timestamp, string userId,
string developerId, string layarName, double radius,
double lat, double lon, double accuracy)
{
LayarModel model = new LayarModel(lat, lon, radius);
return this.JsonNet(model);
}
The part to note that solves your problem specifically is when the ContractResolver on the JsonSerializerSettings is set to use new CamelCasePropertyNamesContractResolver()
This way you never have to set custom naming again.