8

I am trying to create a configuration file using Json that will hold configuration for various types of objects.

Consider this file:

{
    "cameras": [
        {
            "type": "Some.Namespace.CameraClass",
            "assembly": "Some.Assembly",
            "configuration": {
                "ip": "127.0.0.1",
                "port": 8080
            }
        }
    ]
}

At runtime I will use the two "type" and "assembly" properties to construct an object supporting a specific interface, and then I would like to load the configuration into that object.

However, at compile time I do not know the type that "configuration" would map to. I would like to retain it as a json "property" and feed that into the camera object, and then let that object deserialize the json into the right type.

As such I would like to just "carry" the part of the configuration file containing configuration for a particular camera type with me into the object itself, and let it deal with it, treating it like a black box while I carry it like that. The structure of that part should be preserved since I would like full fidelity when creating the configuration types for each camera implementation, even adding subobjects if necessary.

For this particular camera I would configure an IP address and a port, for some other camera I would require authorization data, and for some other camera something completely different.

I would like for the property that would hold this configuration to just get the Json directly, still as a string.

Is this possible?

Here is a LINQPad example that has some bits commented out:

void Main()
{
    const string configurationFile = @"[
    {
        ""type"": ""UserQuery+Camera1"",
        ""configuration"": { ""id"": 10 }
    },
    {
        ""type"": ""UserQuery+Camera2"",
        ""configuration"": { ""name"": ""The second camera"" }
    }
]";
    var cameras = JsonConvert.DeserializeObject<Camera[]>(configurationFile);
    foreach (var camera in cameras)
    {
        var type = Type.GetType(camera.Type);
        var instance = Activator.CreateInstance(type, new object[0]) as ICamera;
        // instance.Configure(camera.Configuration);
    }
}

public class Camera
{
    public string Type { get; set; }
    public JObject Configuration { get; set; }
}

public interface ICamera
{
    void Configure(string json);
}

public class Camera1 : ICamera
{
    private class Configuration
    {
        public int Id { get; set; }
    }

    public void Configure(string json)
    {
        JsonConvert.DeserializeObject<Configuration>(json).Dump();
    }
}

public class Camera2 : ICamera
{
    private class Configuration
    {
        public string Name { get; set; }
    }

    public void Configure(string json)
    {
        JsonConvert.DeserializeObject<Configuration>(json).Dump();
    }
}

The two commented out bits, namely the property in the Camera class, and the call to the Configure method, is what I'd like working.

Is there something I can tag that property with, or some other type I can pick for that property, that would make this work?

I know I can make the property dynamic, which would stuff a JObject into it, but then each Configure method of each camera implementation would have to deal with a JObject and not a known non-dynamic type.

11
  • In what way would you have static typing with your current approach? You don't have any properties of ip and port, or indeed any class representing the configuration anyway. You could potentially try making the property type JObject, but again you wouldn't have intellisense. I'm not sure how you're expecting to get Intellisense for this at all... Commented Jan 12, 2015 at 22:03
  • Inside the specific type being loaded I would take the Json of the configuration and deserialize that into a privately known configuration object. In other words, the external code loading the configuration file would consider this a black box, but the actual camera implementation would be able to deserialize this to a known type. Commented Jan 12, 2015 at 22:04
  • But Camera.Configuration wouldn't be of the concrete type at compile-time, so how would you expect Intellisense to work? If I type camera.Configuration. what properties would you expect to see? As I say, it's possible that JObject would give you what you want, but I haven't tried it. Commented Jan 12, 2015 at 22:06
  • I've appended my hypothetical configuration method. Commented Jan 12, 2015 at 22:06
  • 1
    Why not store the whole camera configuration as a string in the top-level JSON file. And this string happens to be valid JSON when deserialized by the camera... Commented Jan 12, 2015 at 22:10

2 Answers 2

10

It looks like if you use a property of type JObject, that parses but preserves the JSON:

using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;    

public class Foo
{
    public string Name { get; set; }
    public int Age { get; set; }
    public JObject Configuration { get; set; }
}

public class Test
{
   public static void Main()
   {
       var json = File.ReadAllText("test.json");
       var foo = JsonConvert.DeserializeObject<Foo>(json);
       Console.WriteLine(foo.Configuration);
   }

}

Test.json:

{
  "name": "Jon",
  "age": 10,
  "configuration": {
    "ip": "127.0.0.1",
    "port": 8080
  }
}

Output:

{
  "ip": "127.0.0.1",
  "port": 8080
}

I suspect you can deserialize straight from the JObject, but you can always convert it back to a string if you really want to.

Sign up to request clarification or add additional context in comments.

4 Comments

I didn't think of serializing it back to a string. This is application startup, there will be 1-5 cameras typically, one level extra of json magic is not a problem, this seems like a workable solution. Please take a look at the LINQPad example I appended to my question and see if that sparks some new ideas but this seems really good.
In my example, the call to Configure is now this: instance.Configure(JsonConvert.SerializeObject(camera.Configuration));. This seems like the best I can do for the moment.
@LasseV.Karlsen: Have you tried calling ToObject(Type) or TObject<T> on the JObject?
No, I'm investigating the methods of that now and that looks really useful, seems I don't have to convert back and forth at all, though perhaps that is what is being done internally.
-1

If second level configuration is just a string:

{
    "cameras": [
        {
            "type": "Some.Namespace.CameraClass",
            "assembly": "Some.Assembly",
            "configuration": "{ \"ip\": \"127.0.0.1\", \"port\": 8080 }"
        }
    ]
}

This maps to a class:

public class Camera
{
    public string Type { get; set; }
    public string Assembly { get; set; }
    public string Configuration { get; set; }
}

Then you could do a second level deserialize like already shown in your question.

1 Comment

I know I can do this, but I would like to avoid all those escape characters. However, @Jon's answer gave me what I wanted. Yes, I have to take json, deserialize it, serialize a small bit of it back into a json string, then deserialize that another time inside a different class, but I can live with that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.