Parse json secara manual ke kelas POCO
Saya memiliki json di bawah yang perlu saya deserialisasi di 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"
]
}
}
Di bawah ini adalah kelas yang saya buat untuk deserialzie json ke -
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; }
}
- Saya ingin memuat semua
customerSettingsnilai ke dalamCustomerSettingsobjekHolderkelas. - Demikian pula saya ingin memuat semua
clientSettingsnilai ke dalamClientSettingsobjekHolderkelas. - Demikian pula semua
productSettingsnilai menjadiProductSettingsobjekHolderkelas. - Demikian pula
thothTestnilai menjadiThothTestdanosirisTestnilai menjadiOsirisTestobjek.
Catatan: Di atas kelas POCO digunakan untuk bekerja dengan format json lama dan kami deserializing itu langsung menggunakan pustaka Newton JSON tetapi untuk beberapa alasan internal kami sedang mengerjakan mendesain ulang json menjadi json baru yang saya bagikan di atas dalam pertanyaan saya. Dan sekarang dengan desain json baru ini kita tidak bisa langsung deserialisasinya ke kelas POCO saya di atas jadi itu sebabnya saya datang dengan kode di bawah ini untuk melakukannya secara manual. Karena kelas POCO di atas digunakan di banyak tempat jadi itulah mengapa saya tidak ingin menyentuh antarmuka itu.
Di bawah ini adalah kode kerja saya yang secara manual mem-parsing json itu dan mengisi kelas-kelas di atas. Saya ingin melihat apakah ada cara yang dapat kami perbaiki di bawah basis kode karena sepertinya saya mengulangi banyak hal. Saya pikir ini dapat diatur dengan memiliki beberapa metode pembantu yang dapat melakukan pekerjaan untuk saya tetapi tidak dapat menemukan cara untuk melakukan ini?
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
}
Jawaban
Anda bisa memanfaatkan metode ToObject () dari JToken .
Dengan itu logika parsing Anda dapat disederhanakan menjadi ini:
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
- Di sini Anda bisa menggunakan
ToObject<List<Mapping>- maka Anda dapat menggunakan LINQ untuk membuat daftar
ClientSettingsMapping
- maka Anda dapat menggunakan LINQ untuk membuat daftar
productSettings/entries
- Di sini Anda bisa menggunakan
ToObject<List<Mapping>- maka Anda dapat menggunakan LINQ untuk membuat daftar
ProductSettingsMapping
- maka Anda dapat menggunakan LINQ untuk membuat daftar
customerSettings
- Di sini Anda bisa menggunakan
ToObject<List<Mapping>
thothTest dan osirisTest
- Di sini Anda bisa menggunakan
ToObject<Dictionary<int, ISet<string>>>
UPDATE # 1 : Menambahkan metode pembantu untuk mengurangi duplikasi kode
Untuk mengurangi duplikasi kode (pengaturan Klien dan Produk ditangani hampir dengan cara yang sama) kami dapat memperkenalkan metode pembantu berikut:
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;
}
Penggunaannya akan terlihat seperti ini:
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 : Menambahkan implementasi alternatif (menggunakan sintaks ekspresi query linq ) untuk 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();