smiggleworth
smiggleworth

Reputation: 644

Can I deserialize Json with private constructor using System.Text.Json?

Wondering if possible to have private constructors and use the new System.Text.Json serializer.

public class MyModel
{
    public string Name { get; set; }
    public string Data { get; set; }

    private MyModel() 
    {
        // use me for when deserializing
    }

    public MyModel(string name, string data)
    {
        Name = name;
        Data = data;
    }
}

A simple round trip.

var model = new MyModel("doo", "doo");
var json = JsonSerializer.Serialize(model, new JsonSerializerOptions
{
    WriteIndented = true
});

// no to go because of there is no parameterless constructor defined for this object.
var rehydrated = JsonSerializer.Deserialize<MyModel>(json);

Upvotes: 24

Views: 14268

Answers (3)

TanvirArjel
TanvirArjel

Reputation: 32079

Update in .NET 8.0

Just add JsonConstructorAttribute to the private constructor as follows:

public class Employee
{
   [JsonConstructor] // This will work from .NET 8.0
   private Employee()
   {
   }

   private Employee(int id, string name)
   {
     Id = id;
     Name = name;
   }

   [JsonInclude]
   public int Id { get; private set; }

   [JsonInclude]
   public string Name { get; private set; }

   public static Employee Create(int id, string name)
   {
     Employee employee = new Employee(id, name);
     return employee;
   }
}

Update in .NET 7.0

From .NET 7.0, deserialization can be done with a private parameterless constructor by writing your own ContractResolver as follows:

public class PrivateConstructorContractResolver : DefaultJsonTypeInfoResolver
{
   public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
   {
       JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);

       if (jsonTypeInfo.Kind == JsonTypeInfoKind.Object && jsonTypeInfo.CreateObject is null)
       {
         if (jsonTypeInfo.Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Length == 0)
         {
            // The type doesn't have public constructors
            jsonTypeInfo.CreateObject = () => 
                Activator.CreateInstance(jsonTypeInfo.Type, true);
         }
       }

      return jsonTypeInfo;
   }
}

Use as follows:

private static void Main(string[] args)
{
    JsonSerializerOptions options = new JsonSerializerOptions
    {
       TypeInfoResolver = new PrivateConstructorContractResolver()
    };

    Employee employee = Employee.Create(1, "Tanvir");
    string jsonString = JsonSerializer.Serialize(employee);
    Employee employee1 = JsonSerializer.Deserialize<Employee>(jsonString , options);
}

public class Employee
{
   private Employee()
   {
   }

   private Employee(int id, string name)
   {
     Id = id;
     Name = name;
   }

   [JsonInclude]
   public int Id { get; private set; }

   [JsonInclude]
   public string Name { get; private set; }

   public static Employee Create(int id, string name)
   {
     Employee employee = new Employee(id, name);
     return employee;
   }
}

Upvotes: 25

Felipe Augusto
Felipe Augusto

Reputation: 1553

you need to use a JsonConstructor attribute

check the link below

https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-immutability?pivots=dotnet-5-0

Upvotes: 1

S&#39;pht&#39;Kr
S&#39;pht&#39;Kr

Reputation: 2839

It would appear the answer is "No," or at least, "Not Yet".

This is a known limitation of the System.Text.Json serializer for [System.Text.Json] v1. We plan to support this in the future. -ashonkhan

You can write a custom converter for this...For the [ASP.NET Core] 3.0 release, there is no planned additional support for calling a non-default constructor during deserialization. That would have to be done by a custom converter. -steveharter

The custom converter option linked would allow you to use whatever API you do have to build the object, but isn't the same as what, say, Newtonsoft.Json or Entity Framework can do by fiddling with reflection and private constructors, so probably not what you were looking for.

Upvotes: 5

Related Questions