using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace XFEExtension.NetCore.XFETransform.Json; /// /// JSON 对象属性及其惰性值节点。 /// /// 属性名称。 /// 属性值节点。 public readonly record struct XFEJsonProperty(string Name, XFEJsonNode Value); /// /// 引用源 JSON 文本范围的不可变惰性节点。 /// public sealed class XFEJsonNode { private readonly JsonSource _source; private readonly int _candidateStart; private readonly int _depth; private readonly bool _isRoot; private readonly string _path; private readonly object _stateGate = new(); private int _valueStart = -1; private int _end; private XFEJsonValueKind _kind; private ObjectState? _objectState; private ArrayState? _arrayState; internal XFEJsonNode( JsonSource source, int candidateStart, int end, XFEJsonValueKind kind, string path, int depth, bool isRoot = false) { _source = source; _candidateStart = candidateStart; _end = end; _kind = kind; _path = path; _depth = depth; _isRoot = isRoot; if (kind != XFEJsonValueKind.Undefined) _valueStart = JsonScanner.SkipWhitespace(source.Text, candidateStart); } /// /// 当前节点的 JSON 值类型。读取此属性只检查值的首字符,不解析完整值。 /// public XFEJsonValueKind Kind { get { EnsureHeader(); return _kind; } } /// /// 当前节点是否为 JSON null。 /// public bool IsNull => Kind == XFEJsonValueKind.Null; /// /// 当前对象属性数、数组元素数;标量节点读取时引发异常。 /// public int Count => Kind switch { XFEJsonValueKind.Object => GetObjectSnapshot().Length, XFEJsonValueKind.Array => GetArraySnapshot().Length, _ => throw new InvalidOperationException("只有 JSON 对象或数组具有 Count。") }; /// /// 按名称获取对象属性;不存在时返回 。 /// public XFEJsonNode? this[string propertyName] { get { ArgumentNullException.ThrowIfNull(propertyName); return TryGetProperty(propertyName, out var value) ? value : null; } } /// /// 按零基索引获取数组元素;越界时返回 。 /// public XFEJsonNode? this[int index] { get => TryGetElement(index, out var value) ? value : null; } /// /// 尝试获取对象属性。 /// public bool TryGetProperty(string propertyName, [NotNullWhen(true)] out XFEJsonNode? value) { ArgumentNullException.ThrowIfNull(propertyName); EnsureKind(XFEJsonValueKind.Object, "按名称访问属性"); lock (_stateGate) { var state = GetOrCreateObjectState(); if (state.FirstProperties.TryGetValue(propertyName, out value)) return true; while (!state.Completed) { ScanNextObjectProperty(state, propertyName); if (state.FirstProperties.TryGetValue(propertyName, out value)) return true; } } value = null; return false; } /// /// 尝试获取数组元素。 /// public bool TryGetElement(int index, [NotNullWhen(true)] out XFEJsonNode? value) { EnsureKind(XFEJsonValueKind.Array, "按索引访问元素"); if (index < 0) { value = null; return false; } lock (_stateGate) { var state = GetOrCreateArrayState(); while (state.Elements.Count <= index && !state.Completed) ScanNextArrayElement(state, index); if (index < state.Elements.Count) { value = state.Elements[index]; return true; } } value = null; return false; } /// /// 读取并解码字符串;JSON null 返回 。 /// public string? GetString() { if (Kind == XFEJsonValueKind.Null) { EnsureEnd(); return null; } EnsureKind(XFEJsonValueKind.String, "读取字符串"); EnsureEnd(); return JsonScanner.ReadString(_source, ValueStart, _path, out _); } /// /// 读取布尔值。 /// public bool GetBoolean() { EnsureEnd(); return Kind switch { XFEJsonValueKind.True => true, XFEJsonValueKind.False => false, _ => throw Error("当前节点不是布尔值。") }; } /// /// 读取 Int32 数字。 /// public int GetInt32() => GetNumber(int.TryParse, nameof(Int32)); /// /// 读取 Int64 数字。 /// public long GetInt64() => GetNumber(long.TryParse, nameof(Int64)); /// /// 读取 UInt64 数字。 /// public ulong GetUInt64() => GetNumber(ulong.TryParse, nameof(UInt64)); /// /// 读取 Single 数字。 /// public float GetSingle() => GetFloatingNumber(float.TryParse, nameof(Single)); /// /// 读取 Double 数字。 /// public double GetDouble() => GetFloatingNumber(double.TryParse, nameof(Double)); /// /// 读取 Decimal 数字。 /// public decimal GetDecimal() => GetFloatingNumber(decimal.TryParse, nameof(Decimal)); /// /// 将当前节点反序列化为指定类型。 /// public T? GetValue() => Deserialize(); /// /// 尝试将当前节点转换为指定类型。 /// public bool TryGetValue([MaybeNullWhen(false)] out T value) { try { value = Deserialize()!; return true; } catch (XFEJsonException) { value = default; return false; } } /// /// 获取当前节点的原始 JSON 文本。 /// public string GetRawText() { EnsureEnd(); return _source.Text.Substring(ValueStart, _end - ValueStart); } /// /// 枚举对象属性。重复名称会按原顺序保留。 /// public IEnumerable EnumerateObject() => GetObjectSnapshot(); /// /// 枚举数组元素。 /// public IEnumerable EnumerateArray() => GetArraySnapshot(); /// /// 从对象中选择指定属性;缺失属性对应的值为 。 /// public IReadOnlyDictionary SelectProperties(params string[] propertyNames) { ArgumentNullException.ThrowIfNull(propertyNames); EnsureKind(XFEJsonValueKind.Object, "选择属性"); var result = new Dictionary(propertyNames.Length, StringComparer.Ordinal); foreach (var propertyName in propertyNames) { ArgumentNullException.ThrowIfNull(propertyName); result[propertyName] = this[propertyName]; } return result; } /// /// 从数组内每个对象中惰性投影指定属性。 /// public IEnumerable> ProjectArray(params string[] propertyNames) { ArgumentNullException.ThrowIfNull(propertyNames); foreach (var element in EnumerateArray()) yield return element.SelectProperties(propertyNames); } /// /// 将当前节点反序列化为指定类型。 /// public T? Deserialize(XFEJsonOptions? options = null) { var settings = options is null ? _source.Settings : JsonSettings.Create(options); return (T?)JsonCodec.Deserialize(this, typeof(T), settings); } /// /// 校验当前节点的完整 JSON 语法。根节点还会校验其后不存在额外内容。 /// public void Validate() { if (_isRoot) { JsonScanner.ValidateDocument(_source, _path); return; } var end = JsonScanner.SkipValueAtDepth(_source, ValueStart, _depth, _path, out var kind); lock (_stateGate) { if (_end >= 0 && _end != end) throw Error("节点边界与 JSON 值边界不一致。", end); _end = end; _kind = kind; } } /// /// 返回当前节点的原始 JSON 文本。 /// public override string ToString() => GetRawText(); /// /// 从字符串惰性创建 JSON 节点。 /// [return: NotNullIfNotNull(nameof(json))] public static implicit operator XFEJsonNode?(string? json) => json is null ? null : XFEJson.Parse(json); /// /// 链式访问对象属性。 /// public static XFEJsonNode? operator >(XFEJsonNode? node, string propertyName) => node?[propertyName]; /// /// 链式访问对象属性。 /// public static XFEJsonNode? operator <(XFEJsonNode? node, string propertyName) => node?[propertyName]; /// /// 链式访问数组元素。 /// public static XFEJsonNode? operator >(XFEJsonNode? node, int index) => node?[index]; /// /// 链式访问数组元素。 /// public static XFEJsonNode? operator <(XFEJsonNode? node, int index) => node?[index]; internal JsonSource Source => _source; internal int Start => ValueStart; internal int End { get { EnsureEnd(); return _end; } } internal string Path => _path; internal JsonSettings Settings => _source.Settings; internal long ScannedCharacters => _source.ScannedCharacters; internal XFEJsonException Error(string message, int? position = null, Exception? innerException = null) => _source.Error(message, position ?? Math.Max(ValueStart, 0), _path, innerException); private int ValueStart { get { EnsureHeader(); return _valueStart; } } private void EnsureHeader() { if (_valueStart >= 0 && _kind != XFEJsonValueKind.Undefined) { EnsureContainerDepth(); return; } lock (_stateGate) { if (_valueStart >= 0 && _kind != XFEJsonValueKind.Undefined) { EnsureContainerDepth(); return; } _kind = JsonScanner.Classify(_source, _candidateStart, _path, out _valueStart); EnsureContainerDepth(); } } private void EnsureContainerDepth() { if (_kind is XFEJsonValueKind.Object or XFEJsonValueKind.Array && _depth >= _source.Settings.MaxDepth) throw _source.Error($"JSON 嵌套深度超过限制 {_source.Settings.MaxDepth}。", Math.Max(_valueStart, 0), _path); } private void EnsureEnd() { if (_end >= 0) return; lock (_stateGate) { if (_end >= 0) return; _end = JsonScanner.SkipValueAtDepth(_source, ValueStart, _depth, _path, out var kind); _kind = kind; } } private void EnsureKind(XFEJsonValueKind expected, string operation) { if (Kind != expected) throw new InvalidOperationException($"无法对 {Kind} 节点执行“{operation}”;需要 {expected} 节点。"); } private ObjectState GetOrCreateObjectState() { if (_objectState is not null) return _objectState; _objectState = new ObjectState(ValueStart + 1); return _objectState; } private ArrayState GetOrCreateArrayState() { if (_arrayState is not null) return _arrayState; _arrayState = new ArrayState(ValueStart + 1); return _arrayState; } private void ScanNextObjectProperty(ObjectState state, string? requestedPropertyName) { CompletePendingObjectValue(state); var text = _source.Text; var position = JsonScanner.SkipWhitespace(text, state.Cursor); if (!state.HasItems) { if (position < text.Length && text[position] == '}') { state.Completed = true; _end = position + 1; return; } } else { if (position >= text.Length) throw Error("JSON 对象缺少结束大括号。", position); if (text[position] == '}') { state.Completed = true; _end = position + 1; return; } if (text[position] != ',') throw Error("JSON 对象成员之后应为逗号或结束大括号。", position); position = JsonScanner.SkipWhitespace(text, position + 1); if (position < text.Length && text[position] == '}') throw Error("JSON 对象不允许尾逗号。", position); } if (position >= text.Length || text[position] != '"') throw Error("JSON 对象属性名称必须是双引号字符串。", position); var name = JsonScanner.ReadString(_source, position, _path, out position); position = JsonScanner.SkipWhitespace(text, position); if (position >= text.Length || text[position] != ':') throw Error("JSON 属性名称之后缺少冒号。", position); position = JsonScanner.SkipWhitespace(text, position + 1); var childPath = JsonScanner.AppendPropertyPath(_path, name); var kind = JsonScanner.Classify(_source, position, childPath, out var valueStart); var node = new XFEJsonNode(_source, valueStart, -1, kind, childPath, _depth + 1); var property = new XFEJsonProperty(name, node); state.OrderedProperties.Add(property); state.FirstProperties.TryAdd(name, node); state.HasItems = true; state.Cursor = valueStart; state.PendingValue = node; if (!string.Equals(name, requestedPropertyName, StringComparison.Ordinal)) CompletePendingObjectValue(state); } private void ScanNextArrayElement(ArrayState state, int requestedIndex) { CompletePendingArrayValue(state); var text = _source.Text; var position = JsonScanner.SkipWhitespace(text, state.Cursor); if (!state.HasItems) { if (position < text.Length && text[position] == ']') { state.Completed = true; _end = position + 1; return; } } else { if (position >= text.Length) throw Error("JSON 数组缺少结束方括号。", position); if (text[position] == ']') { state.Completed = true; _end = position + 1; return; } if (text[position] != ',') throw Error("JSON 数组元素之后应为逗号或结束方括号。", position); position = JsonScanner.SkipWhitespace(text, position + 1); if (position < text.Length && text[position] == ']') throw Error("JSON 数组不允许尾逗号。", position); } var index = state.Elements.Count; var childPath = $"{_path}[{index}]"; var kind = JsonScanner.Classify(_source, position, childPath, out var valueStart); var node = new XFEJsonNode(_source, valueStart, -1, kind, childPath, _depth + 1); state.Elements.Add(node); state.HasItems = true; state.Cursor = valueStart; state.PendingValue = node; if (index != requestedIndex) CompletePendingArrayValue(state); } private XFEJsonProperty[] GetObjectSnapshot() { EnsureKind(XFEJsonValueKind.Object, "枚举对象属性"); lock (_stateGate) { var state = GetOrCreateObjectState(); while (!state.Completed) ScanNextObjectProperty(state, null); return [.. state.OrderedProperties]; } } private XFEJsonNode[] GetArraySnapshot() { EnsureKind(XFEJsonValueKind.Array, "枚举数组元素"); lock (_stateGate) { var state = GetOrCreateArrayState(); while (!state.Completed) ScanNextArrayElement(state, -1); return [.. state.Elements]; } } private static void CompletePendingObjectValue(ObjectState state) { if (state.PendingValue is null) return; state.Cursor = state.PendingValue.End; state.PendingValue = null; } private static void CompletePendingArrayValue(ArrayState state) { if (state.PendingValue is null) return; state.Cursor = state.PendingValue.End; state.PendingValue = null; } private delegate bool IntegerParser(ReadOnlySpan text, NumberStyles styles, IFormatProvider? provider, out T value); private delegate bool FloatingParser(ReadOnlySpan text, NumberStyles styles, IFormatProvider? provider, out T value); private T GetNumber(IntegerParser parser, string typeName) { EnsureKind(XFEJsonValueKind.Number, $"读取 {typeName}"); EnsureEnd(); var span = _source.Text.AsSpan(ValueStart, _end - ValueStart); if (parser(span, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result)) return result; throw Error($"JSON 数字无法转换为 {typeName}。", ValueStart); } private T GetFloatingNumber(FloatingParser parser, string typeName) { EnsureKind(XFEJsonValueKind.Number, $"读取 {typeName}"); EnsureEnd(); var span = _source.Text.AsSpan(ValueStart, _end - ValueStart); if (parser(span, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) return result; throw Error($"JSON 数字无法转换为 {typeName}。", ValueStart); } private sealed class ObjectState(int cursor) { public int Cursor { get; set; } = cursor; public bool HasItems { get; set; } public bool Completed { get; set; } public XFEJsonNode? PendingValue { get; set; } public Dictionary FirstProperties { get; } = new(StringComparer.Ordinal); public List OrderedProperties { get; } = []; } private sealed class ArrayState(int cursor) { public int Cursor { get; set; } = cursor; public bool HasItems { get; set; } public bool Completed { get; set; } public XFEJsonNode? PendingValue { get; set; } public List Elements { get; } = []; } }