using System.Text.Json; using System.Text.RegularExpressions; using IceImportDAL.DTO.EntityPersistenceAdapter; namespace IceImportBLL.IceImportHttpResponseParser; // Best-effort parsing of a target entity's ResponseStandardDTO JSON response, since the // generic Menu/WebService dispatch can't know the target's exact response DTO shape at compile // time. Save/Update responses follow CLAUDE.md's convention of a message string ending in // "... with id = {id}" (SuccessResponse.SaveSuccessMessage/UpdateSuccessMessage); Get/GetSelectList // responses are matched structurally by property-name convention ("...Id", "...Code", "...Name"). internal static class IceImportHttpResponseParser { private static readonly Regex TrailingId = new(@"(\d+)\s*$", RegexOptions.Compiled); public static int? TryExtractSavedId(string? responseJson) { if (string.IsNullOrWhiteSpace(responseJson)) return null; try { using var doc = JsonDocument.Parse(responseJson); if (!doc.RootElement.TryGetProperty("Body", out var body)) return null; if (body.ValueKind == JsonValueKind.String) { var match = TrailingId.Match(body.GetString() ?? string.Empty); return match.Success && int.TryParse(match.Groups[1].Value, out var id) ? id : null; } if (body.ValueKind == JsonValueKind.Object) return TryExtractIdProperty(body); return null; } catch (JsonException) { return null; } } public static ExistingKeyDTO? TryExtractExistingKey(string? responseJson) { if (string.IsNullOrWhiteSpace(responseJson)) return null; try { using var doc = JsonDocument.Parse(responseJson); if (!doc.RootElement.TryGetProperty("Body", out var body)) return null; var target = body; if (body.ValueKind == JsonValueKind.Array) { using var enumerator = body.EnumerateArray().GetEnumerator(); if (!enumerator.MoveNext()) return null; target = enumerator.Current; } if (target.ValueKind != JsonValueKind.Object) return null; var id = TryExtractIdProperty(target); if (id is null) return null; int? version = null; foreach (var prop in target.EnumerateObject()) { if (string.Equals(prop.Name, "Version", StringComparison.OrdinalIgnoreCase) && prop.Value.ValueKind == JsonValueKind.Number && prop.Value.TryGetInt32(out var v)) { version = v; break; } } return new ExistingKeyDTO { Id = id.Value, Version = version }; } catch (JsonException) { return null; } } // Used by EntityLookupService's Menu/WebService GetSelectList fallback — extracts // (code, name, id) from every object in the Body array, using property-name conventions. public static IEnumerable<(string Code, string Name, int Id)> ExtractCodeNameIdRows(string? responseJson) { if (string.IsNullOrWhiteSpace(responseJson)) yield break; JsonDocument doc; try { doc = JsonDocument.Parse(responseJson); } catch (JsonException) { yield break; } using (doc) { if (!doc.RootElement.TryGetProperty("Body", out var body) || body.ValueKind != JsonValueKind.Array) yield break; foreach (var item in body.EnumerateArray()) { if (item.ValueKind != JsonValueKind.Object) continue; string? code = null, name = null; int? id = null; foreach (var prop in item.EnumerateObject()) { if (id is null && prop.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase) && prop.Value.ValueKind == JsonValueKind.Number && prop.Value.TryGetInt32(out var idVal)) id = idVal; else if (code is null && prop.Name.EndsWith("Code", StringComparison.OrdinalIgnoreCase) && prop.Value.ValueKind == JsonValueKind.String) code = prop.Value.GetString(); else if (name is null && prop.Name.EndsWith("Name", StringComparison.OrdinalIgnoreCase) && prop.Value.ValueKind == JsonValueKind.String) name = prop.Value.GetString(); } if (id is not null) yield return (code ?? string.Empty, name ?? string.Empty, id.Value); } } } private static int? TryExtractIdProperty(JsonElement obj) { foreach (var prop in obj.EnumerateObject()) { if (prop.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase) && prop.Value.ValueKind == JsonValueKind.Number && prop.Value.TryGetInt32(out var id)) return id; } return null; } }