Deserializing with Json.Net, deserialize sub-object into string/similar holding the json?

点点圈 提交于 2021-01-27 04:14:10

问题


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.


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/27911466/deserializing-with-json-net-deserialize-sub-object-into-string-similar-holding

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!