JSON.Net Serialize Object with Root Name

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



Leave a reply



Submit