// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.Json; namespace EpicGames.Core { /// /// Extensions for parsing values out of generic dictionary objects /// public static class JsonElementExtensions { /// /// Checks if the element has a property with the given value /// /// Element to search /// Name of the property /// Expected value of the property /// True if the property exists and matches public static bool HasStringProperty(this JsonElement element, ReadOnlySpan name, string value) { JsonElement property; return element.ValueKind == JsonValueKind.Object && element.TryGetProperty(name, out property) && property.ValueEquals(value); } /// /// Checks if the element has a property with the given value /// /// Element to search /// Name of the property /// Expected value of the property /// True if the property exists and matches public static bool HasStringProperty(this JsonElement element, string name, string value) { JsonElement property; return element.ValueKind == JsonValueKind.Object && element.TryGetProperty(name, out property) && property.ValueEquals(value); } /// /// Gets a property value of a certain type /// /// The element to get a property from /// Name of the element /// The required type of property /// Value of the property /// True if the property exists and was a string public static bool TryGetProperty(this JsonElement element, ReadOnlySpan name, JsonValueKind valueKind, [NotNullWhen(true)] out JsonElement value) { JsonElement property; if (element.TryGetProperty(name, out property) && property.ValueKind == valueKind) { value = property; return true; } else { value = new JsonElement(); return false; } } /// /// Gets a property value of a certain type /// /// The element to get a property from /// Name of the element /// The required type of property /// Value of the property /// True if the property exists and was a string public static bool TryGetProperty(this JsonElement element, string name, JsonValueKind valueKind, [NotNullWhen(true)] out JsonElement value) { JsonElement property; if (element.TryGetProperty(name, out property) && property.ValueKind == valueKind) { value = property; return true; } else { value = new JsonElement(); return false; } } /// /// Gets a string property value /// /// The element to get a property from /// Name of the element /// Value of the property /// True if the property exists and was a string public static bool TryGetStringProperty(this JsonElement element, ReadOnlySpan name, [NotNullWhen(true)] out string? value) { JsonElement property; if (element.TryGetProperty(name, out property) && property.ValueKind == JsonValueKind.String) { value = property.GetString()!; return true; } else { value = null; return false; } } /// /// Gets a string property value /// /// The element to get a property from /// Name of the element /// Value of the property /// True if the property exists and was a string public static bool TryGetStringProperty(this JsonElement element, string name, [NotNullWhen(true)] out string? value) { return TryGetStringProperty(element, Encoding.UTF8.GetBytes(name).AsSpan(), out value); } /// /// Gets a property value from a document or subdocument, indicated with dotted notation /// /// Document to get a property for /// Name of the property /// Receives the nexted element /// True if the property exists and was of the correct type public static bool TryGetNestedProperty(this JsonElement element, ReadOnlySpan name, [NotNullWhen(true)] out JsonElement outElement) { int dotIdx = name.IndexOf((byte)'.'); if (dotIdx == -1) { return element.TryGetProperty(name, out outElement); } JsonElement docValue; if (element.TryGetProperty(name.Slice(0, dotIdx), out docValue) && docValue.ValueKind == JsonValueKind.Object) { return TryGetNestedProperty(docValue, name.Slice(dotIdx + 1), out outElement); } outElement = new JsonElement(); return false; } /// /// Gets a property value from a document or subdocument, indicated with dotted notation /// /// Document to get a property for /// Name of the property /// Receives the nexted element /// True if the property exists and was of the correct type public static bool TryGetNestedProperty(this JsonElement element, string name, [NotNullWhen(true)] out JsonElement outElement) { return TryGetNestedProperty(element, Encoding.UTF8.GetBytes(name).AsSpan(), out outElement); } /// /// Gets an int32 value from the document /// /// Document to get a property for /// Name of the property /// Receives the property value /// True if the property was retrieved public static bool TryGetNestedProperty(this JsonElement element, string name, out int outValue) { JsonElement value; if (element.TryGetNestedProperty(name, out value) && value.ValueKind == JsonValueKind.Number) { outValue = value.GetInt32(); return true; } else { outValue = 0; return false; } } /// /// Gets a string value from the document /// /// Document to get a property for /// Name of the property /// Receives the property value /// True if the property was retrieved public static bool TryGetNestedProperty(this JsonElement element, string name, [NotNullWhen(true)] out string? outValue) { JsonElement value; if (element.TryGetNestedProperty(name, out value) && value.ValueKind == JsonValueKind.String) { outValue = value.GetString(); return outValue != null; } else { outValue = null; return false; } } } }