diff --git a/Core/Data/Result/ConciseModel.cs b/Core/Data/Result/ConciseModel.cs
index a12cf59..c47b7a2 100644
--- a/Core/Data/Result/ConciseModel.cs
+++ b/Core/Data/Result/ConciseModel.cs
@@ -6,6 +6,7 @@
using System.Runtime.Serialization;
using System.Security.Cryptography.X509Certificates;
using System.Text;
+using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
@@ -363,6 +364,15 @@ public bool ContainKeyword(string value, bool ignoreCase = false)
return false;
}
+ ///
+ /// Converts to JSON object.
+ ///
+ /// Options to control the reader behavior during parsing.
+ /// A JSON object instance.
+ /// Its property does not represent a valid single JSON object.
+ public virtual JsonObjectNode ToJson(JsonSerializerOptions options = default)
+ => JsonObjectNode.ConvertFrom(this, options);
+
///
/// Returns a string that represents the current model.
///
diff --git a/Core/Data/Result/DataResult.cs b/Core/Data/Result/DataResult.cs
index a850269..4d11f1d 100644
--- a/Core/Data/Result/DataResult.cs
+++ b/Core/Data/Result/DataResult.cs
@@ -240,6 +240,14 @@ public JsonDataResult(JsonObjectNode data, string message)
[Description("The additional information of the result.")]
public JsonObjectNode AdditionalInfo { get; set; }
+ ///
+ /// Gets or sets the components.
+ ///
+ [DataMember(Name = "components")]
+ [JsonPropertyName("components")]
+ [Description("The components for reference.")]
+ public JsonObjectNode Components { get; set; }
+
///
/// Gets a value indicating whether the data is null.
///
@@ -345,6 +353,57 @@ public IJsonDataNode TryGetValue(string key, string subKey, params string[] keyP
/// The property does not exist.
public IJsonDataNode TryGetValue(ReadOnlySpan key)
=> Data?.TryGetValue(key);
+
+ ///
+ /// Gets schema.
+ ///
+ /// The schema key.
+ /// The schema information.
+ public JsonObjectNode GetSchema(string key)
+ => string.IsNullOrWhiteSpace(key) ? null : GetComponent("schemas")?.TryGetObjectValue(key);
+
+ ///
+ /// Gets reference object.
+ ///
+ /// The type or group key of the resource.
+ /// The identifier of the object.
+ /// The reference object.
+ public JsonObjectNode GetReferenceObject(string type, string id)
+ {
+ if (string.IsNullOrWhiteSpace(id)) return null;
+ var components = GetComponents();
+ if (components == null) return null;
+ var valueKind = components.GetValueKind(type);
+ var json = valueKind switch
+ {
+ JsonValueKind.Object => components.TryGetObjectValue(type)?.TryGetObjectValue(id),
+ JsonValueKind.Array => components.TryGetArrayValue(type)?.TryGetObjectValueById(id),
+ _ => null,
+ };
+ if (json == null) return null;
+ var refPath = json.TryGetStringValue("$ref");
+ if (refPath == null || json.Count != 1) return json;
+ return JsonObjectNode.TryGetRefObjectValue(Data, json, ToJson());
+ }
+
+ internal JsonObjectNode GetComponents()
+ => Components ?? Data?.TryGetObjectValue("components");
+
+ internal JsonObjectNode GetComponent(string key)
+ {
+ var dict = GetComponents();
+ return string.IsNullOrWhiteSpace(key) ? dict : dict?.TryGetObjectValue(key);
+ }
+
+ internal JsonObjectNode ToJson()
+ => new()
+ {
+ { "track", TrackingId },
+ { "message", Message },
+ { "data", Data },
+ { "info", AdditionalInfo },
+ { "components", Components }
+ };
}
///
diff --git a/Core/Net/Http/JsonHttpClient.cs b/Core/Net/Http/JsonHttpClient.cs
index 788e32b..6f46652 100644
--- a/Core/Net/Http/JsonHttpClient.cs
+++ b/Core/Net/Http/JsonHttpClient.cs
@@ -24,6 +24,20 @@
namespace Trivial.Net;
+///
+/// The maker to create JSON HTTP client.
+///
+public interface IJsonHttpClientMaker
+{
+ ///
+ /// Creates a JSON HTTP client.
+ ///
+ /// The type of response.
+ /// An optional callback raised on data received.
+ /// A new JSON HTTP client.
+ JsonHttpClient Create(Action> callback = null);
+}
+
///
/// The event arguments on sending.
///
diff --git a/Core/Security/Token/OAuth.cs b/Core/Security/Token/OAuth.cs
index 5c5a78f..675d680 100644
--- a/Core/Security/Token/OAuth.cs
+++ b/Core/Security/Token/OAuth.cs
@@ -23,7 +23,7 @@ namespace Trivial.Security;
/// The OAuth HTTP web client (for RFC-6749).
/// You can use this to login and then create the JSON HTTP web clients with the authentication information.
///
-public class OAuthClient : TokenContainer
+public class OAuthClient : TokenContainer, IJsonHttpClientMaker
{
///
/// The app accessing key instance.
@@ -699,7 +699,7 @@ private HttpClient CreateHttpClient()
///
/// The OAuth based JSON HTTP web client.
///
-public abstract class OAuthBasedClient : TokenContainer
+public abstract class OAuthBasedClient : TokenContainer, IJsonHttpClientMaker
{
///
/// The OAuth client.
diff --git a/Core/Text/Json/ArrayNode.cs b/Core/Text/Json/ArrayNode.cs
index 9a740d3..3dde043 100644
--- a/Core/Text/Json/ArrayNode.cs
+++ b/Core/Text/Json/ArrayNode.cs
@@ -6,11 +6,13 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
+using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Trivial.Collection;
@@ -1686,6 +1688,41 @@ public bool TryGetObjectValue(int index, out JsonObjectNode result)
return v is not null;
}
+ ///
+ /// Tries to get the value at the specific index.
+ ///
+ /// The identifier of the object.
+ /// The value.
+ public JsonObjectNode TryGetObjectValueById(string id)
+ => TryGetObjectValueById(id, out var p) ? p : null;
+
+ ///
+ /// Tries to get the value at the specific index.
+ ///
+ /// The identifier of the object.
+ /// The result.
+ /// true if has the object; otherwise, false.
+ public bool TryGetObjectValueById(string id, out JsonObjectNode result)
+ {
+ id = id?.Trim();
+ if (string.IsNullOrEmpty(id))
+ {
+ result = null;
+ return false;
+ }
+
+ foreach (var item in store)
+ {
+ if (item is not JsonObjectNode json) continue;
+ if (json.Id?.Trim() != id && json.TryGetStringTrimmedValue("id") != id) continue;
+ result = json;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+
///
/// Tries to get the value at the specific index.
///
@@ -1693,22 +1730,124 @@ public bool TryGetObjectValue(int index, out JsonObjectNode result)
/// The root node.
/// The value.
public JsonObjectNode TryGetRefObjectValue(int index, JsonObjectNode root)
+ => JsonObjectNode.TryGetRefObjectValue(null, TryGetObjectValue(index), root);
+
+ ///
+ /// Tries to get the value at the specific index.
+ ///
+ /// The zero-based index of the element to get.
+ /// The root node.
+ /// The value.
+ public JsonObjectNode TryGetRefObjectValue(int index, JsonDataResult root)
+ => root == null ? null : TryGetRefObjectValue(index, root.ToJson());
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// A list of value matched.
+ public IList WithProperty(string key)
{
- var json = TryGetObjectValue(index);
- if (json == null) return null;
- var refPath = json.TryGetStringValue("$ref");
- if (string.IsNullOrWhiteSpace(refPath)) return json;
- if (root is null || refPath == JsonValues.SELF_REF) return null;
- if (refPath.StartsWith("#/"))
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
{
-#pragma warning disable IDE0057
- var path = refPath.Substring(2).Split('/');
-#pragma warning restore IDE0057
- return root.TryGetObjectValue(path);
+ if (item.ContainsKey(key)) list.Add(item);
}
- if (refPath == "$") return root;
- return root.TryGetValue(refPath, true) as JsonObjectNode;
+ return list;
+ }
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// The value kind of the property.
+ /// A list of value matched.
+ public IList WithProperty(string key, JsonValueKind kind)
+ {
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
+ {
+ if (item.GetValueKind(key) == kind) list.Add(item);
+ }
+
+ return list;
+ }
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// The value of the property.
+ /// A list of value matched.
+ public IList WithProperty(string key, string value)
+ {
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
+ {
+ if (item.TryGetStringValue(key) == value) list.Add(item);
+ }
+
+ return list;
+ }
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// The value of the property.
+ /// One of the enumeration values that specifies how the strings will be compared.
+ /// A list of value matched.
+ public IList WithProperty(string key, string value, StringComparison comparisonType)
+ {
+ if (value == null) return WithProperty(key, value);
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
+ {
+ if (value.Equals(item.TryGetStringValue(key), comparisonType)) list.Add(item);
+ }
+
+ return list;
+ }
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// The value of the property.
+ /// A list of value matched.
+ public IList WithProperty(string key, int value)
+ {
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
+ {
+ if (item.TryGetInt32Value(key) == value) list.Add(item);
+ }
+
+ return list;
+ }
+
+ ///
+ /// Filters by the given property.
+ ///
+ /// The property key.
+ /// The value of the property.
+ /// A list of value matched.
+ public IList WithProperty(string key, bool value)
+ {
+ var list = new List();
+ var col = SelectObjects();
+ foreach (var item in col)
+ {
+ if (item.TryGetBooleanValue(key) == value) list.Add(item);
+ }
+
+ return list;
}
///
@@ -4050,6 +4189,13 @@ public override string ToString()
public string ToString(IndentStyles indentStyle)
=> ConvertToString(indentStyle, 0);
+ ///
+ /// Filters by getting JSON object list only.
+ ///
+ /// A list of value matched.
+ internal IList SelectObjects()
+ => store.OfType().ToList();
+
///
/// Gets the JSON array format string of the value.
///
@@ -4952,6 +5098,57 @@ public static JsonArrayNode TryParse(string json, JsonDocumentOptions options =
return null;
}
+ ///
+ /// Tries to parse a string to a JSON array.
+ ///
+ /// A file with JSON array string content to parse.
+ /// Options to control the reader behavior during parsing.
+ /// A JSON array instance; or null, if error format.
+ public static JsonArrayNode TryParse(FileInfo file, JsonDocumentOptions options = default)
+ {
+ try
+ {
+ if (file == null || !file.Exists) return null;
+ using var stream = file.OpenRead();
+ return Parse(stream, options);
+ }
+ catch (ArgumentException)
+ {
+ }
+ catch (InvalidOperationException)
+ {
+ }
+ catch (JsonException)
+ {
+ }
+ catch (FormatException)
+ {
+ }
+ catch (InvalidCastException)
+ {
+ }
+ catch (IOException)
+ {
+ }
+ catch (SecurityException)
+ {
+ }
+ catch (UnauthorizedAccessException)
+ {
+ }
+ catch (NullReferenceException)
+ {
+ }
+ catch (AggregateException)
+ {
+ }
+ catch (ExternalException)
+ {
+ }
+
+ return null;
+ }
+
///
/// Converts an object to JSON object.
///
diff --git a/Core/Text/Json/ObjectNode.cs b/Core/Text/Json/ObjectNode.cs
index afa1b2f..8dc8be1 100644
--- a/Core/Text/Json/ObjectNode.cs
+++ b/Core/Text/Json/ObjectNode.cs
@@ -16,6 +16,7 @@
using Trivial.Data;
using Trivial.Maths;
+using Trivial.Net;
using Trivial.Reflection;
using Trivial.Web;
@@ -184,7 +185,7 @@ private JsonObjectNode(IDictionary copy, bool threadSafe
///
public string Id
{
- get => TryGetStringValue("$id");
+ get => TryGetStringValue("$id")?.Trim();
set => SetValueOrRemove("$id", value);
}
@@ -2650,22 +2651,16 @@ public bool TryGetObjectValue(IEnumerable keyPath, out JsonObjectNode re
/// The root node.
/// The value.
public JsonObjectNode TryGetRefObjectValue(string key, JsonObjectNode root)
- {
- root ??= this;
- var json = TryGetObjectValue(key);
- if (json == null) return null;
- var refPath = json.TryGetStringValue("$ref");
- if (string.IsNullOrWhiteSpace(refPath)) return json;
- if (refPath == JsonValues.SELF_REF) return this;
- if (refPath.StartsWith("#/"))
- {
- var path = refPath.Substring(2).Split('/');
- return root.TryGetObjectValue(path);
- }
+ => TryGetRefObjectValue(this, TryGetObjectValue(key), root);
- if (refPath == "$") return root;
- return root.TryGetValue(refPath, true) as JsonObjectNode;
- }
+ ///
+ /// Tries to get the value of the specific property.
+ ///
+ /// The property key.
+ /// The root node.
+ /// The value.
+ public JsonObjectNode TryGetRefObjectValue(string key, JsonDataResult root)
+ => root == null ? null : TryGetRefObjectValue(key, root.ToJson());
///
/// Tries to get the value of the specific property.
@@ -7629,7 +7624,7 @@ public static JsonObjectNode TryParse(FileInfo file, JsonDocumentOptions options
/// The object to convert.
/// Options to control the reader behavior during parsing.
/// A JSON object instance.
- /// json does not represent a valid single JSON object.
+ /// obj or its property does not represent a valid single JSON object.
public static JsonObjectNode ConvertFrom(object obj, JsonSerializerOptions options = default)
{
if (obj is null) return null;
@@ -7714,6 +7709,78 @@ public static JsonObjectNode ConvertFrom(object obj, JsonSerializerOptions optio
return !leftValue.Equals(rightValue);
}
+ ///
+ /// Tries to get the value of the specific property.
+ ///
+ /// The source.
+ /// The property.
+ /// The root node.
+ /// The value.
+ internal static JsonObjectNode TryGetRefObjectValue(JsonObjectNode source, JsonObjectNode property, JsonObjectNode root)
+ {
+ root ??= source;
+ if (property == null) return null;
+ var refPath = property.TryGetStringValue("$ref");
+ if (refPath == null) return property;
+ if (string.IsNullOrWhiteSpace(refPath) || refPath == "#") return root;
+ if (refPath == JsonValues.SELF_REF) return source;
+ if (refPath.StartsWith("#/"))
+ {
+ var path = refPath.Substring(2).Split('/');
+ return root?.TryGetObjectValue(path);
+ }
+
+ if (refPath == "$") return root;
+ if (refPath.StartsWith("./"))
+ {
+ try
+ {
+ var file = new FileInfo(refPath);
+ return TryParse(file);
+ }
+ catch (ArgumentException)
+ {
+ }
+ catch (UnauthorizedAccessException)
+ {
+ }
+ catch (SecurityException)
+ {
+ }
+ catch (NotSupportedException)
+ {
+ }
+ catch (IOException)
+ {
+ }
+ catch (ExternalException)
+ {
+ }
+
+ return property;
+ }
+
+ if (refPath.StartsWith("http") && refPath.Contains("://"))
+ {
+ property = new JsonObjectNode
+ {
+ { "$ref", refPath }
+ };
+ _ = TryParse(null, refPath, property);
+ return property;
+ }
+
+ return root?.TryGetValue(refPath, true) as JsonObjectNode;
+ }
+
+ internal static async Task TryParse(JsonHttpClient http, string url, JsonObjectNode json)
+ {
+ var resp = await (http ?? new()).GetAsync(url);
+ if (json == null) json = new();
+ else json.Clear();
+ json.SetRange(resp);
+ }
+
private static JsonObjectNode TryGetObjectValueByProperty(JsonObjectNode json, string key)
{
if (json.GetValueKind(key) == JsonValueKind.Array)