Reputation: 93
I begin c# and Deserialize Json. In my training I studied Newtonsoft, but I would like to do the same with system.text.json
With this json, I want to select
To make a object list.
https://api.nvidia.partners/edge/product/search?page=1&limit=9&locale=fr-fr&category=GPU&gpu=RTX%203090,RTX%203080%20Ti,RTX%203080,RTX%203070%20Ti,RTX%203070,RTX%203060%20Ti,RTX%203060&gpu_filter=RTX%203090~12,RTX%203080%20Ti~7,RTX%203080~16,RTX%203070%20Ti~3,RTX%203070~18,RTX%203060%20Ti~8,RTX%203060~2,RTX%202080%20SUPER~1,RTX%202080~0,RTX%202070%20SUPER~0,RTX%202070~0,RTX%202060~6,GTX%201660%20Ti~0,GTX%201660%20SUPER~9,GTX%201660~8,GTX%201650%20Ti~0,GTX%201650%20SUPER~3,GTX%201650~17
Class
public class CarteGraphique
{
public string displayName { get; set; }
public string prdStatus { get; set; }
public List<Retailer> retailers { get; set; }
}
With Newtonsoft, I do the following:
Newtonsoft
JObject jsonParse = JObject.Parse(json);
IList<CarteGraphique> products = new List<CarteGraphique>();
IList<JToken> productDetailsParse = jsonParse["searchedProducts"]["productDetails"]
.Children()
.Where(n => n["isFounderEdition"].Value<bool>() == true)
.ToList();
var featuredProductParse = jsonParse["searchedProducts"]["featuredProduct"];
foreach (JToken item in productDetailsParse)
{
CarteGraphique result = item.ToObject<CarteGraphique>();
products.Add(result);
}
var featuredProduct = featuredProductParse.ToObject<CarteGraphique>();
products.Add(featuredProduct);
foreach (var item in products)
{
Console.WriteLine(item.DisplayName);
}
I want to do that with System.Text.Json...but I don't know how select json part "productDetails" for add this in a object list.
System.text.json
var listGpu = new List<CarteGraphique>();
var jsonParse = JsonDocument.Parse(json);
var jsonFilter = jsonParse.RootElement
.GetProperty("searchedProducts")
.GetProperty("featuredProduct")
.GetRawText();
var jsonObj = JsonSerializer.Deserialize<CarteGraphique>(jsonFilter);
listGpu.Add(jsonObj);
foreach (var item in listGpu)
{
Console.WriteLine(item.displayName);
}
Can you help me please? Documentation is not clear for the beginner that i am.
Upvotes: 8
Views: 6901
Reputation: 101
I also have faced the same inconvinieces when moved to JsonDocument. So I have developed a small library to re-create the same behavior JObject had. It allows to navigate domain object model using indexer-like syntax. It is called JsonEasyNavigation, you can find it on github or get from nuget.org.
In your case, the solution could be the following:
List<CarteGraphique> products = new List<CarteGraphique>();
using var jsonDocument = JsonDocument.Parse(json);
var nav = jsonDocument.ToNavigation();
var productDetails = nav["searchedProducts"]["productDetails"].Values
.Where(x => x["isFounderEdition"].GetBooleanOrDefault())
.Select(x => x.Map<CarteGraphique>())
.ToArray();
var featuredProduct = nav["searchedProducts"]["featuredProduct"].Map<CarteGraphique>();
products.AddRange(productDetails);
products.Add(featuredProduct);
JsonDocument have to be transformed into the JsonNavigationElement using ToNavigation() method. I hope you will find it useful.
Upvotes: 1
Reputation: 117036
You can emulate the Json.NET logic as follows:
using var jsonParse = JsonDocument.Parse(json); // Be sure to dispose the JsonDocument!
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var products = jsonParse.RootElement
.GetProperty("searchedProducts") // Get the searchedProducts value
.GetProperty("productDetails") // Get the productDetails value
.EnumerateArray() // Enumerate its items
.Where(n => n.GetProperty("isFounderEdition").GetBoolean()) // Filter on those for which isFounderEdition == true
.Select(n => n.ToObject<CarteGraphique>(options)) // Deserialize to a CarteGraphique
.ToList();
// Add the searchedProducts.featuredProduct item to the list.
var featuredProduct = jsonParse.RootElement
.GetProperty("searchedProducts")
.GetProperty("featuredProduct")
.ToObject<CarteGraphique>(options);
products.Add(featuredProduct);
Where ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
is an extension method from this answer to System.Text.Json.JsonElement ToObject workaround:
public static partial class JsonExtensions
{
public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
{
var bufferWriter = new System.Buffers.ArrayBufferWriter<byte>();
using (var writer = new Utf8JsonWriter(bufferWriter))
element.WriteTo(writer);
return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
}
public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null)
{
if (document == null)
throw new ArgumentNullException(nameof(document));
return document.RootElement.ToObject<T>(options);
}
}
Notes:
JsonDocument
is disposable. Failure to dispose of it will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework, according to the docs.
In .NET 6.0 Microsoft plans to implement direct deserialization from a JsonElement
, see We should be able serialize and deserialize from DOM #31274. In the meantime the above extension method can be used.
I did not add any checking for missing properties. If properties might be missing, use JsonElement.TryGetProperty()
. See Getting nested properties with System.Text.Json for some extension methods that can make that easier.
Unlike Json.NET, System.Text.Json is case-sensitive by default. To enable case-insensitive deserialization use JsonSerializerOptions { PropertyNameCaseInsensitive = true }
as shown in JsonSerializer.Deserialize fails.
Demo fiddle here.
Upvotes: 8