6

I have a class property set, and fixed in the constructor, example below is a Guid.NewGuid()..

The rationale being to only allow the class to populate, on creation.. so it's set in the constructor, and marked only as get; - works fine, until i serialize / deserialize to Json.

It seems that because there is no set; the deserialization fails.. and the constructor steps in and creates a new guid

I have tried internal, private, and protected set .. but all result in a regeneration of the property, the only way i can get it to work is by marking Id as {get;set;},

class Foo
{
    public Foo() => Id = Guid.NewGuid().ToString();
    public string Id { get; set; }
}

which then means you can simply do:

var obj = new Foo();
obj.Id = "Any Old Rubbish";

Is there a way around this at all?

Short sample:

class Program
{
    static void Main()
    {
        var obj = new Foo();
        Console.WriteLine(obj.Id);

        var serialize = JsonConvert.SerializeObject(obj);
        var deserialize = JsonConvert.DeserializeObject<Foo>(serialize);
        Console.WriteLine(deserialize.Id);

        Console.ReadLine();
    }
}

class Foo
{
    public Foo() => Id = Guid.NewGuid().ToString();

    public string Id { get; }
}

Output:

7868a298-f20d-4719-85ef-ba64c8658819
34fe422c-9555-4d17-ae1a-9fbf21af1b71
m1nkeh
  • 1,337
  • 23
  • 45
  • Better show your your `Foo` class code instead of describing it in words. – Yeldar Kurmangaliyev Feb 28 '18 at 12:24
  • it is there on the original post? edit: ah, i see what you mean, updated! minuscule change, didn't think it was necessary.... – m1nkeh Feb 28 '18 at 12:24
  • I found [this](https://i.stack.imgur.com/HB4kE.png) image of how ```JsonConvert.DeserializeObject```'s algorithm works. A detailed explanation can be found [here](https://stackoverflow.com/questions/41870132/how-does-json-deserialization-in-c-sharp-work). So would you mind changing your code like so? ```var obj = new Foo(Guid.NewGuid().ToString());``` & ```public Foo(String Id) => this.Id = Id;``` Like this it will work due to the constructor call won't regenerate a guid. – Bee Feb 28 '18 at 12:47
  • 1
    @m1nkeh your code doesn't show any protected properties. It shows a read-only property. That's already supported by JSON.NET using various techniques – Panagiotis Kanavos Feb 28 '18 at 13:06
  • You can use a constructor with parameters marked with the [JsonConstructor](https://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm) attribute. You can use the [JsonProperty ](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm) to mark a private property, field or property with a private setter for deserialization as well – Panagiotis Kanavos Feb 28 '18 at 13:18
  • @PanagiotisKanavos sorry, a private setter? just for deserialization? – m1nkeh Feb 28 '18 at 13:23
  • 1
    @m1nkeh which just one of the reasons such things exist. *read-only* does mean read-only. Only the constructor can set a read-only property. Anyway, pick your technique. You can have a constructor with parameters, a private setter or a get-only property with a serialized private field – Panagiotis Kanavos Feb 28 '18 at 13:26
  • pretty sure private setter also does not work, so i think we are at the last option (get-only prop w/ serialized private field), it's the how part, can you flesh out the post with an example at all? – m1nkeh Feb 28 '18 at 13:29
  • ohhh i get it, literally make it {get; private set; } and add [JsonProperty] ?? – m1nkeh Feb 28 '18 at 13:32

1 Answers1

6

The following image (it is not done by me but is found in this question) shows how the algorithm for JsonConvert.DeserializeObjectworks. As you can see the constructor of the serialized type is called almost always. That's why you have to use a constructor with parameters as the left most path of the image shows.

JsonConvert.DeserializeObject algorithm

The following shows a modification of your example using a constructor with parameters.

class Program
{
  static void Main()
  {
    var obj = new Foo(Guid.NewGuid().ToString());
    Console.WriteLine(obj.Id);

    var serialize = JsonConvert.SerializeObject(obj);
    var deserialize = JsonConvert.DeserializeObject<Foo>(serialize);
    Console.WriteLine(deserialize.Id);

    Console.ReadLine();
  }
}

class Foo
{
  public Foo(String Id) => this.Id = Id;        
  public string Id { get; }
}

This will result in having the same guid before and after serialization while there is no need to use public string Id { get; set;} as you didn't want to.

Bee
  • 1,306
  • 2
  • 10
  • 24
  • 5
    This is documented and you don't even need to remove the default constructor. You can use the JsonConstructor attribute to specify *which* constructor to use. You can use `JsonProperty` to specify that even a private property, field or property with a private setter should be deserialized – Panagiotis Kanavos Feb 28 '18 at 13:13