Analyser manuellement json en classes POCO

Oct 06 2020

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 la Holderclasse.
  • De même, je veux charger toutes les clientSettingsvaleurs dans l' ClientSettingsobjet de la Holderclasse.
  • De même toutes les productSettingsvaleurs en ProductSettingsobjet de Holderclasse.
  • De même thothTestvaleurs dans ThothTestet osirisTestvaleurs dans OsirisTestobjet.

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

6 PeterCsala Oct 06 2020 at 15:04

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

productSettings/entries

  • Ici vous pouvez utiliser ToObject<List<Mapping>
    • alors vous pouvez utiliser LINQ pour construire une liste de ProductSettingsMapping

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