1

I am using .NET's System.Text.Json to deserialize json into an immutable class. I want to hide the constructor from the class user to prevent creating instances of this class and only create instances when deserializing. How can I make System.Text.Json use private constructor which is not default?

I want to do something like this:

public class Sheet
    {
        public string Id { get; }
        public IEnumerable<IEnumerable<SheetCell>> Data { get; }

        [JsonConstructor]
        private Sheet(string id, IEnumerable<IEnumerable<SheetCell>> data)
        {
            Id = id;
            Data = data;
        }
    }
Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • Can you please show some example. – Guru Stron Apr 17 '23 at 13:13
  • 1
    [This question](https://stackoverflow.com/q/58147552/87698) shows how to do it with a private parameterless constructor and writable properties. Maybe a similar technique would work in your case? – Heinzi Apr 17 '23 at 13:22
  • @Heinzi if it can work, I am unsure on how to do it. – Justas Mileika Apr 17 '23 at 13:32
  • " I want to hide the constructor from the class user to prevent creating instances of this class" IMHO it doesn't make any sense. who are the users? Nobody can change it using a web browser. Only you and another software developers that can change your class any way they want. Are you trying to hide it from herself? – Serge Apr 17 '23 at 16:06
  • @Serge I am trying to hide the constructor from the developers that would use the library. Is this not the recommended thing to do? – Justas Mileika Apr 17 '23 at 16:16
  • And what happens if they use the constructor? – Serge Apr 17 '23 at 16:31
  • Do you think that software developers too stupid to create a json string and using it to create a class object as you trying to do? – Serge Apr 17 '23 at 16:40
  • @Serge So what I have is json array with custom values and I want those values in array to have a reference to the array (that is needed for the value to get processed into something else based on other array values). I add this array reference to the array values while deserializing the array. I want to remove the constructor from the value in the array because the value logically cannot exists without the array it is in. – Justas Mileika Apr 17 '23 at 16:41
  • I am sorry I just try to understand why one software developer trying to make difficult the work of another software developer even if he makes some problems for himself too. I still don't hear from you why do you think somebody will create an instance of another class if it is useless? And what is wrong if somebody try? – Serge Apr 17 '23 at 16:48
  • 2
    @Serge very often I will think about my own library code and how it will be used by the end user even if the end user is myself. A good API will make it difficult to do things the wrong way and guide you and make it easy to use the right way. If you expose too much, then you may forget things later and reproduce code that was already in the library code or do things that are dangerous or hacky just because it's available. – TJ Rockefeller Apr 17 '23 at 16:52
  • I don't think I am making the life of another developer difficult. On the contrary, I think it makes their life easier as it limits the usage of the library to only the operations that are valid. I wouldn't want a library with documentation that says 'Don't call this constructor, it doesn't work properly'. – Justas Mileika Apr 17 '23 at 16:53

1 Answers1

1

If you don't want the end user to create instances of your serialization class often you can handle that using a data transfer object (DTO) pattern. In your library code you have internal classes for serialization and deserialization, and then you have a separate public class that is exposed to the user.

//use this class internally for serializing and deserializing
internal class SheetDto
{
    public string Id { get; init; }
    public IEnumerable<IEnumerable<SheetCell>> Data { get; init; }
}

//the end user only has access to this public class.
public class Sheet
{
    public string Id { get; }
    public IEnumerable<IEnumerable<SheetCell>> Data { get; }

    private Sheet(SheetDto dto)
    {
        Id = dto.Id;
        Data = dto.Data;
    }

    public static Sheet Deserialize()
    {
        //do whatever deserializtion logic you need to get a SheetDto
        //if deserializing from a file, maybe this method takes in a
        //filepath
        var dto = someMethodToDeserialize();
        return new Sheet(dto);
    }
}
TJ Rockefeller
  • 3,178
  • 17
  • 43