2015-01-12 3 views
6

Sto provando a creare un file di configurazione usando Json che manterrà la configurazione per vari tipi di oggetti.Deserializzare con Json.Net, deserializzare l'oggetto secondario in stringa/simile che contiene il json?

Considerate questo file:

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

In runtime Userò il "tipo" due e "assemblaggio" proprietà per costruire un oggetto che supporta un'interfaccia specifica, e quindi vorrei caricare la configurazione in cui oggetto.

Tuttavia, al momento della compilazione non conosco il tipo a cui la "configurazione" verrebbe associata. Mi piacerebbe conservarlo come una "proprietà" json e inserirlo nell'oggetto della telecamera, e quindi lasciare che quell'oggetto deserializzi il json nel tipo giusto.

Come tale mi piacerebbe semplicemente "trasportare" la parte del file di configurazione che contiene la configurazione per un particolare tipo di telecamera con me nell'oggetto stesso, e lasciare che si occupi di esso, trattandolo come una scatola nera mentre porto così. La struttura di quella parte dovrebbe essere preservata dal momento che mi piacerebbe la massima fedeltà durante la creazione dei tipi di configurazione per ogni implementazione della telecamera, aggiungendo anche i sottooggetti se necessario.

Per questa particolare telecamera configurerei un indirizzo IP e una porta, per alcune altre fotocamere avrei bisogno di dati di autorizzazione e per qualche altra fotocamera qualcosa di completamente diverso.

Mi piacerebbe che la proprietà mantenga questa configurazione per ottenere direttamente Json, sempre come una stringa.

È possibile?

Ecco un esempio di LINQPad che ha alcuni pezzi commentata:

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(); 
    } 
} 

I due commentate bit, vale a dire la proprietà nella classe Camera, e la chiamata al metodo Configure, è quello che mi piacerebbe come lavorare

C'è qualcosa che possa taggare quella proprietà con, o qualche altro tipo che posso scegliere per quella proprietà, che farebbe funzionare questo?

so di poter rendere la struttura dinamica, che roba un JObject in esso, ma poi ognuno Configure metodo per ogni implementazione fotocamera sarebbe avere a che fare con un JObject e non un tipo noto non dinamica.

+0

In che modo avresti una digitazione statica con il tuo attuale approccio? Non hai alcuna proprietà di 'ip' e' port', o comunque di una classe che rappresenta comunque la configurazione. Potresti * potenzialmente provare a creare il tipo di proprietà 'JObject', ma di nuovo non avresti intellisense. Non sono sicuro di come ti aspetti di ottenere Intellisense per questo ... –

+0

All'interno del tipo specifico che sto caricando, prenderei il Json della configurazione e lo deserializzerò in un oggetto di configurazione noto a priori. In altre parole, il codice esterno che carica il file di configurazione considererebbe questa una scatola nera, ma l'implementazione effettiva della fotocamera sarebbe in grado di deserializzare questo a un tipo noto. –

+0

Ma 'Camera.Configuration' non sarebbe di tipo concreto in fase di compilazione, quindi come ti aspetteresti che Intellisense funzioni? Se digito 'camera.Configurazione 'quali proprietà ti aspetteresti di vedere? Come ho detto, è possibile che JObject ti desse quello che vuoi, ma non l'ho provato. –

risposta

8

Sembra che se si utilizza una proprietà di tipo JObject, che analizza ma conserva la 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 
    } 
} 

uscita:

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

ho il sospetto è possibile deserializzare direttamente dal JObject, ma si può sempre riconvertirlo in un string se si vuole veramente.

+0

Non ho pensato di serializzarlo su una stringa. Questo è l'avvio dell'applicazione, ci saranno 1-5 telecamere in genere, un livello extra di magia JSON non è un problema, questa sembra una soluzione praticabile. Si prega di dare un'occhiata all'esempio LINQPad ho aggiunto alla mia domanda e vedere se questo spunti alcune nuove idee, ma questo sembra davvero buono. –

+0

Nel mio esempio, la chiamata a 'Configure' è ora questa:' instance.Configure (JsonConvert.SerializeObject (camera.Configuration)); '. Questo sembra il meglio che posso fare per il momento. –

+0

@ LasseV.Karlsen: Hai provato a chiamare 'ToObject (Type)' o 'TObject ' su 'JObject'? –

0

Se la configurazione secondo livello è solo una stringa:

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

Questo associa a una classe:

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

allora si potrebbe fare un secondo livello di deserializzare come già dimostrato nella sua interrogazione.

+0

So che posso farlo, ma vorrei evitare tutti quei personaggi di fuga. Tuttavia, la risposta di @ Jon mi ha dato quello che volevo. Sì, devo prendere JSON, deserializzare, serializzare un po 'di esso in una stringa json, poi deserializzare un'altra volta in una classe diversa, ma posso conviverci. –