feat(commons): native-typed JSON for List values; Decode reads both forms
This commit is contained in:
@@ -25,11 +25,10 @@ public static class AttributeValueCodec
|
||||
case string s: return s; // already canonical
|
||||
case IFormattable f: return f.ToString(null, CultureInfo.InvariantCulture);
|
||||
case IEnumerable e:
|
||||
var items = e.Cast<object?>()
|
||||
.Select(x => x is IFormattable xf
|
||||
? xf.ToString(null, CultureInfo.InvariantCulture)
|
||||
: x?.ToString());
|
||||
return JsonSerializer.Serialize(items, JsonOpts);
|
||||
// Native-typed JSON: serialize the runtime collection so System.Text.Json emits
|
||||
// numbers/bools unquoted, strings quoted, and DateTime as ISO-8601. Boxed as object
|
||||
// so STJ uses the runtime element type. STJ numbers/dates are culture-invariant.
|
||||
return JsonSerializer.Serialize<object>(e, JsonOpts);
|
||||
default: return value.ToString();
|
||||
}
|
||||
}
|
||||
@@ -46,18 +45,25 @@ public static class AttributeValueCodec
|
||||
if (elementType is null)
|
||||
throw new FormatException("List attribute requires an element type.");
|
||||
|
||||
string?[] raw;
|
||||
try { raw = JsonSerializer.Deserialize<string?[]>(value) ?? []; }
|
||||
JsonElement[] raw;
|
||||
try { raw = JsonSerializer.Deserialize<JsonElement[]>(value) ?? []; }
|
||||
catch (JsonException ex) { throw new FormatException("Malformed list JSON.", ex); }
|
||||
|
||||
var clrType = ElementClrType(elementType.Value);
|
||||
var listType = typeof(List<>).MakeGenericType(clrType);
|
||||
var result = (IList)Activator.CreateInstance(listType)!;
|
||||
foreach (var item in raw)
|
||||
result.Add(ParseScalar(item, elementType.Value));
|
||||
foreach (var el in raw)
|
||||
result.Add(ParseScalar(JsonElementToString(el), elementType.Value));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string? JsonElementToString(JsonElement el) => el.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => el.GetString(), // old form, or string-typed lists
|
||||
JsonValueKind.Null => null, // ParseScalar throws "may not be null"
|
||||
_ => el.GetRawText() // number/bool → "10" / "1.5" / "true"
|
||||
};
|
||||
|
||||
private static Type ElementClrType(DataType t) => t switch
|
||||
{
|
||||
DataType.String => typeof(string),
|
||||
|
||||
Reference in New Issue
Block a user