0

I would like to achieve something actually quite simple, but I keep running into walls. I have a class that should give access to a REST-API. Such an ApiClient should be created on basis of a certain class of my program via constructor. So far, so simple.

Now my first idea was to embed the corresponding data models for the JSON serialization as a private class inside this outer class. But then it came to my mind that Newtonsoft JSON cannot see private members. So I thought I would make the classes public and somehow hide the constructors of these data classes to the outside so that they are only visible from the class ApiClient. But in C# the access modifier friend, known from VB.net, is missing. But I don't want to set all data classes public either, because they are basically irrelevant for the rest of the program.

Is there a way to implement this somehow so that it doesn't get too complicated? I have already seen approaches with a private class and a public interface. But I don't think that's very nice either. I also saw an approach using data annotation to make the properties visible to Newtonsoft JSON anyways, but that didn't look too attractive to me either.

To say it in short: I want to transfer data to a REST-API and encapsulate the whole thing in a class. I want to use a class for the data that has to be transferred. But if possible this class should only be visible for the ApiClient class and the serializer.

Is there a best-practice solution for this rather common task?

public class ApiClient {
    private ApiClientData _serializableData;

    // This class should only be visible to the ApiClient class and the serializer
    private class ApiClientData {
        public string Data1 {get; private set;}
        public int Data2 {get; private set;}

        public ApiClientData(SomeOtherDataModel input) {
            Data1 = input.SomeVal;
            Data2 = input.OtherVal;
        }
    }

    public ApiClient(SomeOtherDataModel baseData) {
        _serializableData = new ApiClientData(baseData);
    }

    public void CallService() {
        // Newtonsoft JSON
        string jsonString = JsonConvert.SerializeObject(_serializableData);

        CallBlackBoxFunctionToCommunicateWithTheRestServer(jsonString);
    }
}

public void main() {
    SomeOtherDataModel data = GetDataFromSql();
    ApiClient client = new ApiClient(data);
    client.CallService();
}
André Reichelt
  • 1,484
  • 18
  • 52
  • `friend` in VB is called `internal` in C# – Gimly Sep 16 '20 at 11:52
  • @Gimly But with friend, you can do more. There is no way in c# to control, for example, that a method can only be called from within the same namespace. – André Reichelt Sep 16 '20 at 11:54
  • 1
    So typically what you do with APIs published as a library is have an `IClient`, a concrete `ApiClient` and all models are public POCOs. You send POCOs in and get POCOs out. All the weird private etc. logic does not live in those models. – zaitsman Sep 16 '20 at 11:54
  • @Gimly that is exactly what `internal` does - internals can only be called from the same Assembly, unless you add a `InternalsVisibleTo` attribute – zaitsman Sep 16 '20 at 11:55
  • @zaitsman So there is no way to prevent the construction of my data objects from outside my ApiClient class? – André Reichelt Sep 16 '20 at 11:56
  • Ahm, those would be `internal` – zaitsman Sep 16 '20 at 12:01
  • @zaitsman `internal` relates to the assembly, not the namespace or even the class. Because my whole project only consists of a single assembly, using `internal` would be useless. – André Reichelt Sep 16 '20 at 12:03
  • 1
    If your project consists of a single assembly, AND you want to encapsulate this logic as much, you're doing it wrong :) – zaitsman Sep 16 '20 at 12:12
  • 1
    To be fair, your question is far from clear, and your code sample doesn't really help. Could you try to describe more precisely what you're trying to achieve and where you are blocked? – Gimly Sep 16 '20 at 12:16
  • @Gimly I find it difficult to describe it more precisely. I just want to transfer data to a REST-API and encapsulate the whole thing in a class. I want to use a class for the data that has to be transferred. But if possible this class should only be visible for the `ApiClient` class and the serializer. – André Reichelt Sep 16 '20 at 12:52
  • 1
    *it came to my mind that Newtonsoft JSON cannot see `private` members* - it can if you mark them with `[JsonProperty]` or with data contract attributes. See [Why are some members missing when trying to print an object by serializing to JSON?](https://stackoverflow.com/a/48157367/3744182) for a list of options for serializing nonpublic members. And Json.NET is perfectly happy to serialize and deserialize private **types**. – dbc Sep 16 '20 at 16:13

0 Answers0