Skip to content

Commit 3380a04

Browse files
dmealingclaude
andcommitted
feat(csharp): canonical serializer emits attrs always-inline (D5 corpus parity)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5a30975 commit 3380a04

1 file changed

Lines changed: 14 additions & 75 deletions

File tree

server/csharp/MetaObjects/SerializerJson.cs

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ namespace MetaObjects;
1919
/// </summary>
2020
public static class SerializerJson
2121
{
22-
// Java int range — used for distinguishing int vs long attr subtypes.
23-
private const long JavaIntMax = 2_147_483_647L;
24-
private const long JavaIntMin = -2_147_483_648L;
25-
2622
// ---------------------------------------------------------------------------
2723
// Public API
2824
// ---------------------------------------------------------------------------
@@ -34,7 +30,7 @@ public static class SerializerJson
3430
/// </summary>
3531
public static string CanonicalSerialize(MetaData model)
3632
{
37-
var nodeObj = SerializeNode(model, inlineAttrs: true, effective: false);
33+
var nodeObj = SerializeNode(model, effective: false);
3834
var sorted = SortAttrKeys(nodeObj)!;
3935
return Stringify(sorted) + "\n";
4036
}
@@ -47,7 +43,7 @@ public static string CanonicalSerialize(MetaData model)
4743
/// </summary>
4844
public static string CanonicalSerializeEffective(MetaData model)
4945
{
50-
var nodeObj = SerializeNode(model, inlineAttrs: true, effective: true);
46+
var nodeObj = SerializeNode(model, effective: true);
5147
var sorted = SortAttrKeys(nodeObj)!;
5248
return Stringify(sorted) + "\n";
5349
}
@@ -68,28 +64,6 @@ private static string Stringify(JsonNode node)
6864
return node.ToJsonString(JsonOpts);
6965
}
7066

71-
// ---------------------------------------------------------------------------
72-
// Infer attr subType from a runtime AttrValue (for child-node form)
73-
// ---------------------------------------------------------------------------
74-
75-
private static string InferAttrSubType(object? value)
76-
{
77-
return value switch
78-
{
79-
IReadOnlyList<string> => Constants.ATTR_SUBTYPE_STRINGARRAY,
80-
bool => Constants.ATTR_SUBTYPE_BOOLEAN,
81-
long l => (l >= JavaIntMin && l <= JavaIntMax)
82-
? Constants.ATTR_SUBTYPE_INT
83-
: Constants.ATTR_SUBTYPE_LONG,
84-
double d => Number.IsIntegerDouble(d)
85-
? ((long)d >= JavaIntMin && (long)d <= JavaIntMax
86-
? Constants.ATTR_SUBTYPE_INT
87-
: Constants.ATTR_SUBTYPE_LONG)
88-
: Constants.ATTR_SUBTYPE_DOUBLE,
89-
_ => Constants.ATTR_SUBTYPE_STRING,
90-
};
91-
}
92-
9367
// ---------------------------------------------------------------------------
9468
// Build the fused "type.subType" wrapper key
9569
// ---------------------------------------------------------------------------
@@ -101,15 +75,15 @@ private static string FusedKey(string type, string subType)
10175
// Serialize a single node → { "<type>.<subType>": { ...body } }
10276
// ---------------------------------------------------------------------------
10377

104-
private static JsonObject SerializeNode(MetaData model, bool inlineAttrs, bool effective)
78+
private static JsonObject SerializeNode(MetaData model, bool effective)
10579
{
106-
var inner = SerializeNodeInner(model, inlineAttrs, effective);
80+
var inner = SerializeNodeInner(model, effective);
10781
var wrapper = new JsonObject();
10882
wrapper.Add(FusedKey(model.Type, model.SubType), inner);
10983
return wrapper;
11084
}
11185

112-
private static JsonObject SerializeNodeInner(MetaData model, bool inlineAttrs, bool effective)
86+
private static JsonObject SerializeNodeInner(MetaData model, bool effective)
11387
{
11488
// Canonical body-key order:
11589
// 1. name 2. package 3. extends 4. abstract
@@ -150,58 +124,23 @@ private static JsonObject SerializeNodeInner(MetaData model, bool inlineAttrs, b
150124
var childList = effective ? model.Children() : model.OwnChildren();
151125
var attrMap = effective ? model.Attrs() : model.OwnAttrs();
152126

153-
// Walk children: structural children recurse; attr children emit either as
154-
// inline @-attrs or as child {"attr.*": {...}} nodes.
155-
var emittedAsChild = new HashSet<string>(StringComparer.Ordinal);
127+
// Walk children: structural children recurse. Attrs are ALWAYS emitted
128+
// inline as @-attrs (never as child {"attr.*": {...}} nodes), so attr-typed
129+
// children are skipped here — the C# parser dual-stores each attr both as a
130+
// child node and as a parent attr (Parser.cs:950-951), and the inline loop
131+
// below emits it from attrMap.
156132
var serializedChildren = new JsonArray();
157133

158134
foreach (var child in childList)
159135
{
160-
if (child.Type != Constants.TYPE_ATTR)
161-
{
162-
serializedChildren.Add(SerializeNode(child, inlineAttrs, effective));
163-
continue;
164-
}
165-
166-
// Emit attr as child node form
167-
var attrName = child.Name;
168-
var attrValue = child.OwnAttr(Constants.RESERVED_KEY_VALUE);
169-
var attrSubType = child.SubType != Constants.SUBTYPE_BASE
170-
? child.SubType
171-
: InferAttrSubType(attrValue ?? "");
172-
173-
var attrBody = new JsonObject
174-
{
175-
{ Constants.RESERVED_KEY_NAME, JsonValue.Create(attrName) },
176-
{ Constants.RESERVED_KEY_VALUE, AttrValueToJsonNode(attrValue) },
177-
};
178-
var attrWrapper = new JsonObject();
179-
attrWrapper.Add(FusedKey(Constants.TYPE_ATTR, attrSubType), attrBody);
180-
serializedChildren.Add(attrWrapper);
181-
emittedAsChild.Add(attrName);
136+
if (child.Type == Constants.TYPE_ATTR) continue;
137+
serializedChildren.Add(SerializeNode(child, effective));
182138
}
183139

184-
// Inline @-attrs: emit attrs NOT already emitted as child nodes.
140+
// Inline @-attrs — always emitted as @name; attrs have no child form.
185141
foreach (var (attrName, attrValue) in attrMap)
186142
{
187-
if (emittedAsChild.Contains(attrName)) continue;
188-
189-
if (inlineAttrs)
190-
{
191-
obj.Add($"{Constants.ATTR_PREFIX}{attrName}", AttrValueToJsonNode(attrValue));
192-
}
193-
else
194-
{
195-
var subType = InferAttrSubType(attrValue);
196-
var attrBody = new JsonObject
197-
{
198-
{ Constants.RESERVED_KEY_NAME, JsonValue.Create(attrName) },
199-
{ Constants.RESERVED_KEY_VALUE, AttrValueToJsonNode(attrValue) },
200-
};
201-
var attrWrapper = new JsonObject();
202-
attrWrapper.Add(FusedKey(Constants.TYPE_ATTR, subType), attrBody);
203-
serializedChildren.Add(attrWrapper);
204-
}
143+
obj.Add($"{Constants.ATTR_PREFIX}{attrName}", AttrValueToJsonNode(attrValue));
205144
}
206145

207146
// children — emit only if non-empty

0 commit comments

Comments
 (0)