问题
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