CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using NUnit.Framework; // Throws NUnit.Framework.AssertionException
//https://stackoverflow.com/questions/61553962/getting-nested-properties-with-system-text-json
public static partial class JsonExtensions
{
public static JsonElement? Get(this JsonElement element, string name) =>
element.ValueKind != JsonValueKind.Null && element.ValueKind != JsonValueKind.Undefined && element.TryGetProperty(name, out var value)
? value : (JsonElement?)null;
public static JsonElement? Get(this JsonElement element, int index)
{
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
return null;
// Throw if index < 0
return index < element.GetArrayLength() ? element[index] : null;
}
}
class TestClass
{
public static void Test()
{
TestArray();
var raw = @"{
""data"": {
""products"": {
""edges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
}
]
}
}
}";
Console.WriteLine("Testing JSON with keys present:");
TestGood(raw);
Test(raw);
Console.WriteLine("\nTesting JSON with keys missing:");
var bad1 = @"{
""data"": {
""products"": {
""XXedges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
}
]
}
}
}";
Test(bad1);
var bad2 = @"{
""data"": {
""products"": {
""edges"": []
}
}
}";
Test(bad2);
var bad3 = @"{
""data"": {
""products"": {
""edges"": null
}
}
}";
Test(bad3);
var bad4 = @"{
""data"": {
""products"": {
""edges"": [null]
}
}
}";
Test(bad4);
}
static void TestGood(string raw)
{
var doc = JsonSerializer.Deserialize<JsonElement>(raw);
Assert.Throws<KeyNotFoundException>(() => doc.GetProperty("does-not-exist"));
var node = doc.Get("data")?.Get("products")?.Get("edges")?[0].Get("node");
Assert.IsNotNull(node);
Assert.AreEqual(null, doc.Get("data")?.Get("XXXXproducts")?.Get("edges")?[0].Get("node"));
Assert.AreEqual(null, doc.Get("data")?.Get("products")?.Get("XXXedges")?[0].Get("node"));
Assert.AreEqual(null, doc.Get("data")?.Get("products")?.Get("edges")?[0].Get("XXXnode"));
}
static void Test(string raw)
{
var doc = JsonSerializer.Deserialize<JsonElement>(raw);
Assert.Throws<KeyNotFoundException>(() => doc.GetProperty("does-not-exist"));
var node = doc.Get("data")?.Get("products")?.Get("edges")?.Get(0)?.Get("node");
var productIdString = node?.Get("id")?.GetString();
var originalSrcString = node?.Get("featuredImage")?.Get("originalSrc")?.GetString();
Int64? someIntegerValue = node?.Get("Size")?.GetInt64(); // You could use "var" here also, I used Int64? to make the inferred type explicit.
Console.WriteLine("productIdString={0}, originalSrcString={1}, someIntegerValue={2}", productIdString, originalSrcString, someIntegerValue);
}
static void TestArray()
{
var arrayJson = "[0]";
var arrayDoc = JsonSerializer.Deserialize<JsonElement>(arrayJson);
Assert.AreEqual(0, arrayDoc.Get(0).Value.GetInt32());
Assert.IsNull(arrayDoc.Get(1));
Assert.Throws<IndexOutOfRangeException>(() => arrayDoc.Get(-1));
}
}
public static partial class JsonExtensions
{
public static JsonElement? Get(this JsonDocument doc, string name) => doc.RootElement.Get(name);
public static JsonElement? Get(this JsonDocument doc, int index) => doc.RootElement.Get(index);
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
public static string GetNetCoreVersion()
{
//https://techblog.dorogin.com/how-to-detect-net-core-version-in-runtime-ecd65ad695be
//
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];
return null;
}
}