How can I add a custom root node when serializing an object with JSON.NET?
Here's a solution specifically for Web API, which I am also using: RootFormatter.cs
I wrote it based on Creating a JSONP Formatter for ASP.NET Web API.
Instead of using a custom attribute I am reusing Title field of JsonObjectAttribute
. Here's a usage code:
using Newtonsoft.Json
[JsonObject(Title = "user")]
public class User
{
public string mail { get; set; }
}
Then, add RootFormatter to your App_Start and register it as follows in WebApiConfig
:
GlobalConfiguration.Configuration.Formatters.Insert(0, new RootFormatter());
I was able to get a wrapped response similar to WCF's WebMessageBodyStyle.Wrapped
:
{"user":{
"mail": "foo@example.com"
}}
How to add a root node to a JSON in C# using Json.NET?
I suppose you have user
object. Just use anonymous class to add extra root node:
var obj = new { user = user };
string json = JsonConvert.SerializeObject(obj);
The resulting JSON will look like that:
{
"user": {.../your user object/...}
}
Serialize object using JSON.net when json object has $ref to root object list
This is what the PreserveReferencesHandling
is intended for:
// ...
var settings = new JsonSerializerSettings {
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
var json = JsonConvert.SerializeObject(project, settings);
var projectFromJson = JsonConvert.DeserializeObject<Project>(json);
Console.WriteLine(ReferenceEquals(projectFromJson.Templates[1],
projectFromJson.Tasks.First().Template));
// This outputs "True"
Note that Json.NET uses its own reference syntax rather than JSONpath. The JSON for your object hierarchy would look as follows:
{
"$id": "1",
"Templates": [
{
"$id": "2",
"Name": "Temp1"
},
{
"$id": "3",
"Name": "Temp2"
},
{
"$id": "4",
"Name": "Temp3"
}
],
"Tasks": [
{
"$id": "5",
"Template": {
"$ref": "3"
}
}
]
}
JSON.Net Serialize list with root name
Objects do not have names in JSON, properties do. (See JSON.org.) Therefore, if you want to name an object, you'll have to make it the value of a property of another containing object.
var x = insList.Select(a => new
{
rootName = new
{
ac = a.CreatedDate,
bd = a.CreatedBy
}
});
This will yield the following JSON:
[
{
"rootName": {
"ac": "0001-01-01T00:00:00",
"bd": 0
}
},
{
"rootName": {
"ac": "0001-01-01T00:00:00",
"bd": 0
}
},
{
"rootName": {
"ac": "0001-01-01T00:00:00",
"bd": 0
}
}
]
json.net - how to add property $type ONLY on root object
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.
Newtonsoft.Json Custom Root Name for Deserialization
I have a central deserialization method, so I'm trying to avoid type specific code as much as possible.
I used the following to resolve the problem, maybe not as sexy as I was hoping for but it works.
public class ResultType
{
public ResultDetailType result { get; set; }
}
public class ResultDetailType
{
public bool status { get; set; }
public string message { get; set; }
}
Include Class name as part of the serialization in JSON C#
Is this solution for specifying the concrete type is a must or is it just your proposed solution?
Because Json.NET has a built-in support for encoding the actual types for properties of interface types or base classes:
var json = JsonConvert.SerializeObject(myContract, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
If you really need a completely custom logic you have to implement a converter, which also can be passed to the JsonSerializerSettings
. It must be derived from JsonConverter
and you have to implement the WriteJson
method to emit your desired json sting using low level tokens just like in case of an XmlWriter
:
private class MyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IAddressPoint).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var address = (IAddressPoint)value;
writer.WriteStartObject(); // {
writer.WritePropertyName($"geo:{address.GetType().Name}"); // "geo:StreetAddress"
// ... etc.
writer.WriteEndObject(); // }
// or you can just emit raw string:
writer.WriteRaw(myJsonBody);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
{
// todo: and the deserialization part goes here
}
}
Related Topics
How to Get My C# Program to Sleep for 50 Msec
How Does Wcf Deserialization Instantiate Objects Without Calling a Constructor
How to Intercept All Key Events, Including Ctrl+Alt+Del and Ctrl+Tab
Dynamically Setting CSS Values Using ASP.NET
How to Write an Apple Push Notification Provider in C#
Invoke C# Code from JavaScript in a Document in a Webbrowser
What Are Major Differences Between C# and Java
Closing Excel Application Process in C# After Data Access
Using C# Reflection to Call a Constructor
Serialization of Entity Framework Objects with One to Many Relationship
Determine the File Type Using C#
How to Serialize/Deserialize a Dictionary with Custom Keys Using JSON.Net
Why Doesn't Include Have Any Effect
How to Loop Through a Date Range
.Net Code to Send Zpl to Zebra Printers