Analyser manuellement json en classes POCO
J'ai ci-dessous json dans lequel je dois désérialiser C#-
{
"clientSettings":[
{
"clientId":12345,
"entries":[
{
"key":"abc",
"value":false
},
{
"key":"def",
"value":false
},
{
"key":"ghi",
"value":false
}
]
},
{
"clientId":9876,
"entries":[
{
"key":"lkmn",
"value":false
}
]
}
],
"productSettings":[
{
"productId":11,
"entries":[
{
"key":"jkl",
"value":true
},
{
"key":"mno",
"value":true
}
]
},
{
"productId":12,
"entries":[
{
"key":"jkl",
"value":true
},
{
"key":"mno",
"value":true
}
]
}
],
"customerSettings":[
{
"key":"enableData",
"value":false
},
{
"key":"minPriceValue",
"value":"10.28"
},
{
"key":"presentData",
"value":"AEGIS"
}
],
"thothTest":{
"9876":[
"K"
],
"5431":[
"A",
"L"
],
"5123":[
"L"
]
},
"osirisTest":{
"7678":[
"K"
]
}
}
Voici les classes que j'ai créées pour désérialiser json en -
public class ProcessHolder : Holder
{
public IDictionary<int, ISet<string>> OsirisTest { get; set; }
public IDictionary<int, ISet<string>> ThothTest { get; set; }
}
public class Holder
{
public IList<Mapping> CustomerSettings { get; set; }
public IList<ClientSettingsMapping> ClientSettings { get; set; }
public IList<ProductSettingsMapping> ProductSettings { get; set; }
}
public class Mapping
{
public string Key { get; set; }
public object Value { get; set; }
}
public class ProductSettingsMapping : Mapping
{
public int ProductId { get; set; }
}
public class ClientSettingsMapping : Mapping
{
public int ClientId { get; set; }
}
- Je veux charger toutes les
customerSettingsvaleurs dans l'CustomerSettingsobjet de laHolderclasse. - De même, je veux charger toutes les
clientSettingsvaleurs dans l'ClientSettingsobjet de laHolderclasse. - De même toutes les
productSettingsvaleurs enProductSettingsobjet deHolderclasse. - De même
thothTestvaleurs dansThothTestetosirisTestvaleurs dansOsirisTestobjet.
Remarque: les classes POCO ci-dessus fonctionnaient avec l'ancien format json et nous le désérialisions directement à l'aide de la bibliothèque Newton JSON, mais pour une raison interne, nous travaillons à la refonte du json en nouveau json, ce que j'ai partagé ci-dessus dans ma question. Et maintenant, avec ce nouveau design json, nous ne pouvons pas le désérialiser directement dans mes classes POCO ci-dessus, c'est pourquoi j'ai proposé le code ci-dessous pour le faire manuellement. Comme les classes POCO ci-dessus sont utilisées à de nombreux endroits, c'est pourquoi je ne veux pas toucher à cette interface.
Voici mon code de travail qui analyse manuellement ce json et remplit les classes ci-dessus. Je voulais voir s'il y avait un moyen d'améliorer la base de code ci-dessous car il semble que je répète beaucoup de choses. Je pense que cela peut être organisé en ayant des méthodes d'aide qui peuvent faire le travail pour moi mais pas en mesure de comprendre comment faire cela?
public static void Main(string[] args)
{
var jsonContent = File.ReadAllText("/beta/Downloads/test.json");
var parsed = JObject.Parse(jsonContent);
var parsedClientSettings = parsed["clientSettings"];
List<ClientSettingsMapping> clientSettings = new List<ClientSettingsMapping>();
foreach (var parsedClientSetting in parsedClientSettings)
{
var clientId = parsedClientSetting.Value<int>("clientId");
foreach (var entry in parsedClientSetting["entries"])
{
clientSettings.Add(new ClientSettingsMapping { ClientId = clientId, Key = entry["key"].ToString(), Value = entry["value"].ToString() });
}
}
var parsedproductSettings = parsed["productSettings"];
List<ProductSettingsMapping> productSettings = new List<ProductSettingsMapping>();
foreach (var parsedproductSetting in parsedproductSettings)
{
var productId = parsedproductSetting.Value<int>("productId");
foreach (var entry in parsedproductSetting["entries"])
{
productSettings.Add(new ProductSettingsMapping { ProductId = productId, Key = entry["key"].ToString(), Value = entry["value"].ToString() });
}
}
var parsedCustomerSettings = parsed["customerSettings"];
List<Mapping> customerSettings = new List<Mapping>();
foreach (var entry in parsedCustomerSettings)
{
customerSettings.Add(new Mapping { Key = entry["key"].ToString(), Value = entry["value"].ToString() });
}
var parsedthothTests = parsed["thothTest"];
IDictionary<int, ISet<string>> thothTest = new Dictionary<int, ISet<string>>();
foreach (JProperty x in (JToken)parsedthothTests)
{
int name = int.Parse(x.Name);
JToken value = x.Value;
ISet<string> values = new HashSet<string>();
foreach (var val in value)
{
values.Add((string)val);
}
thothTest.Add(name, values);
}
var parsedOsirisTests = parsed["osirisTest"];
IDictionary<int, ISet<string>> osirisTest = new Dictionary<int, ISet<string>>();
foreach (JProperty x in (JToken)parsedOsirisTests)
{
int name = int.Parse(x.Name);
JToken value = x.Value;
ISet<string> values = new HashSet<string>();
foreach (var val in value)
{
values.Add((string)val);
}
osirisTest.Add(name, values);
}
ProcessHolder processHolder = new ProcessHolder() { ClientSettings = clientSettings, ProductSettings = productSettings, CustomerSettings = customerSettings, ThothTest = thothTest, OsirisTest = osirisTest };
// now use "processHolder" object here
}
Réponses
Vous pouvez profiter de la méthode ToObject () du JToken .
Avec cela, votre logique d'analyse pourrait être simplifiée à ceci:
var jsonContent = File.ReadAllText("/beta/Downloads/test.json");
var parsed = JObject.Parse(jsonContent);
//ClientSettings needs special handling
var parsedClientSettings = parsed["clientSettings"];
var clientSettings = new List<ClientSettingsMapping>();
foreach (var parsedClientSetting in parsedClientSettings)
{
var clientId = parsedClientSetting.Value<int>("clientId");
var entries = parsedClientSetting["entries"].ToObject<List<Mapping>>();
clientSettings.AddRange(entries
.Select(entry => new ClientSettingsMapping { ClientId = clientId, Key = entry.Key, Value = entry.Value }));
}
//ProductSettings needs special handling
var parsedproductSettings = parsed["productSettings"];
var productSettings = new List<ProductSettingsMapping>();
foreach (var parsedproductSetting in parsedproductSettings)
{
var productId = parsedproductSetting.Value<int>("productId");
var entries = parsedproductSetting["entries"].ToObject<List<Mapping>>();
productSettings.AddRange(entries
.Select(entry => new ProductSettingsMapping {ProductId = productId, Key = entry.Key, Value = entry.Value}));
}
var processHolder = new ProcessHolder
{
ClientSettings = clientSettings,
ProductSettings = productSettings,
CustomerSettings = parsed["customerSettings"].ToObject<List<Mapping>>(),
ThothTest = parsed["thothTest"].ToObject<Dictionary<int, ISet<string>>>(),
OsirisTest = parsed["osirisTest"].ToObject<Dictionary<int, ISet<string>>>()
};
clientSettings/entries
- Ici vous pouvez utiliser
ToObject<List<Mapping>- alors vous pouvez utiliser LINQ pour construire une liste de
ClientSettingsMapping
- alors vous pouvez utiliser LINQ pour construire une liste de
productSettings/entries
- Ici vous pouvez utiliser
ToObject<List<Mapping>- alors vous pouvez utiliser LINQ pour construire une liste de
ProductSettingsMapping
- alors vous pouvez utiliser LINQ pour construire une liste de
customerSettings
- Ici vous pouvez utiliser
ToObject<List<Mapping>
thothTest et osirisTest
- Ici vous pouvez utiliser
ToObject<Dictionary<int, ISet<string>>>
MISE À JOUR # 1 : Ajout d'une méthode d'assistance pour réduire la duplication de code
Afin de réduire la duplication de code (les paramètres du client et du produit sont traités presque de la même manière), nous pouvons introduire la méthode d'assistance suivante:
private List<T> ParseSettings<T>(JToken parsed, string settingsNodeName, string idNodeName, Func<(int id, string key, object value), T> mapper)
{
var rawSettings = parsed[settingsNodeName];
var settings = new List<T>();
foreach (var setting in rawSettings)
{
var id = setting.Value<int>(idNodeName);
var entries = setting["entries"].ToObject<List<Mapping>>();
settings.AddRange(entries
.Select(entry => mapper((id, entry.Key, entry.Value))));
}
return settings;
}
Son utilisation ressemblerait à ceci:
var productSettings = ParseSettings(parsed, "productSettings", "productId",
mapping=> new ProductSettingsMapping {ProductId = mapping.id, Key = mapping.key, Value = mapping.value});
var clientSettings = ParseSettings(parsed, "clientSettings", "clientId",
mapping=> new ClientSettingsMapping {ClientId = mapping.id, Key = mapping.key, Value = mapping.value});
UPDATE # 2 : Ajout d'une implémentation alternative (utilise la syntaxe d'expression de requête linq ) pour ParseSettings:
private static List<T> ParseSettings<T>(JToken parsed, string settingsNodeName, string idNodeName,
Func<(int id, string key, object value), T> mapper)
=>
(from setting in parsed[settingsNodeName]
let id = setting.Value<int>(idNodeName)
let entries = setting["entries"].ToObject<List<Mapping>>()
from entry in entries
select mapper((id, entry.Key, entry.Value)))
.ToList();