question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature request: Serialization attributes without adding Json.NET as a dependency

See original GitHub issue

Although some data contract attributes can be used as substitutes for Json.NET specific attributes, there are some, like [JsonConstructor], that requires the Json.NET to be added as a dependency.

Why is this a problem?

Most core/dto/model/etc projects tend to expose types that are required to be serializable without doing any serialization themselves. This makes adding a serialization library as a dependency redundant since many of these types can get away with using the data contract attributes. Data contract attributes can be found in this lightweight, System.Runtime.Serialization.Primitives package and are already included in the full .NET framework.

So if I were to create a library with a class that has multiple constructors and want my users to be able to serialize it using Json.NET, I need to depend on Json.NET because I need the [JsonConstructor]. This is an unnecessary dependency for my users that don’t use Json.NET and an even worse one for my users that do, because now they need to wait for me to update my library in order to update their Json.NETs.

What can be done?

Json.NET can compare attribute names instead of doing full type checks. For example, this line in the DefaultContractResolver can check if the constructor has an attribute named “JsonConstructor” without minding which assembly it comes from, so the users can create and use their own [JsonConstructor]s. I’ve seen this approach work with ReSharper annotations and some Redgate utilities.

Unfortunately, [JsonConstructor] is the only Json.NET specific attribute I know that doesn’t accept a parameter. Letting the users define their own attributes would involve more reflection for attributes like [JsonObject].

One other option I can think of is the .NET route. The attributes can be refactored into another package (e.g. Newtonsoft.Json.Primitives) that is also a dependency of the Json.NET itself. Consisting only of annotations, it can have a much slower release cycle while being considerably smaller in size.

What should be done?

I think reading the properties of a user-defined attribute via reflection (each time, or by using cached expression trees) may increase the project’s complexity a little too much, and introducing a new dependency in the form of a primitives package may be considered overkill.

Allowing user-defined [JsonConstructor]s, on the other hand, seem easy enough and that is the one Json.NET specific attribute I find myself using the most (To compare, I needed to use the [JsonObject] only once in my life). For me, the only thing that the data contract attributes lack is a way to choose a constructor. It is somewhat understandable though, since the DataContractSerializer does not call any constructor.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:2
  • Comments:6

github_iconTop GitHub Comments

2reactions
dropsoniccommented, Jan 8, 2019

I faced the same problem, so I’d like to share my 5 cents here. If your objects are immutable, they don’t have a parameterless constructor, but they often have a constructor that accepts all or some of the property values. In this case, it would be nice if we can instruct JSON.NET to use the most specific constructor available without adding any attributes. E.g. it can be done by adding a new value to the ConstructorHandling enumeration.

1reaction
dropsoniccommented, Jan 9, 2019

@safakgur yes, my bad It doesn’t work if there are several constructors with parameters.

Right now the desired behavior can be achieved by using a custom ContractResolver:

public class MostSpecificConstructorContractResolver : DefaultContractResolver
{
	protected override JsonObjectContract CreateObjectContract(Type objectType)
	{
		var contract = base.CreateObjectContract(objectType);
		
		if (!objectType.IsAbstract && !objectType.IsInterface 
			&& contract.DefaultCreator == null && contract.OverrideCreator == null)
		{
			var constructor = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				.OrderBy(c => c.GetParameters().Length)
				.LastOrDefault();

			if (constructor != null)
			{
				contract.OverrideCreator = a => constructor.Invoke(a);
				foreach (var parameter in CreateConstructorParameters(constructor, contract.Properties))
				{
					contract.CreatorParameters.AddProperty(parameter);
				}
			}
		}

		return contract;
	}
}

Usage:

JsonConvert.DeserializeObject<TestEntity>(json,
    new JsonSerializerSettings()
    {
         ContractResolver = new MostSpecificConstructorContractResolver()
    });
Read more comments on GitHub >

github_iconTop Results From Across the Web

Is there a way to ignore get-only properties in Json.NET ...
To conditionally serialize a property, add a method that returns boolean with the same name as the property and then prefix the method...
Read more >
Migrate from Newtonsoft.Json to System.Text.Json - .NET
System. Text. Json provides the following ways to ignore properties or fields while serializing: The [JsonIgnore] attribute on a property ...
Read more >
Serialize and deserialize JSON using C# - .NET
This overview describes the System.Text.Json namespace functionality for serializing to and deserializing from JSON in .NET.
Read more >
Using multiple JSON serialization settings in ASP.NET Core
First, we need a way to specify multiple JSON serialization settings, and access these settings by name. Fortunately, ASP.NET Core got us ...
Read more >
Despite .NET Core 3 Deprecation, Newtonsoft JSON ...
The tool to serialize objects or value types to JSON (JavaScript Object Notation) and to deserialize JSON into objects or value types is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found