The v0.9 spec distinguishes two intents on updateDataModel:
"value": <x> (key is present): replace the value at path with x.
value key absent: remove the key at path.
UpdateDataModel.fromJson collapses both into a single Object? value field:
|
/// Creates a [UpdateDataModel] message from a JSON map. |
|
factory UpdateDataModel.fromJson(JsonMap json) { |
|
return UpdateDataModel( |
|
surfaceId: json[surfaceIdKey] as String, |
|
path: DataPath(json['path'] as String? ?? '/'), |
|
value: json['value'], |
|
); |
|
} |
and toJson() drops the key with 'value': ?value. The wire-level distinction is lost as soon as the message hits the model.
|
/// Converts this message to a JSON map. |
|
Map<String, dynamic> toJson() => { |
|
'version': 'v0.9', |
|
surfaceIdKey: surfaceId, |
|
'path': path.toString(), |
|
'value': ?value, |
|
}; |
Expressed in a code example:
final message = UpdateDataModel.fromJson({
'surfaceId': 's1',
'path': '/x',
'value': null,
});
final serialized = message.toJson();
expect(serialized.containsKey('value'), isTrue); // fails. value key is dropped.
A deeper problem is that the data model also can't distinguish between "set to null" and "remove". There's no method to set a key to null. Source:
final model = InMemoryDataModel();
model.update(DataPath.root, {'x': 1, 'y': 2});
model.update(DataPath('/x'), null);
// Expected per 0.9 spec: {'x': null, 'y': 2}
// Actual: {'y': 2}
So even if UpdateDataModel.fromJson parsed the distinction correctly, the data model can't set a field to null:
|
if (current is Map) { |
|
if (remaining.isEmpty) { |
|
if (value == null) { |
|
current.remove(segment); |
|
} else { |
|
current[segment] = value; |
|
} |
|
return; |
|
} |
I think the most practical impact of this issue would manifest in UIs that use tri-state fields (e.g. false, true, unset/reset). Not very common, but worth fixing in my opinon. To fix, I suggest splitting the setter API into a setter and a remover, then have MessageProcessor branch on hasValue.
The v0.9 spec distinguishes two intents on
updateDataModel:"value": <x>(key is present): replace the value atpathwithx.valuekey absent: remove the key atpath.UpdateDataModel.fromJsoncollapses both into a singleObject? valuefield:genui/packages/genui/lib/src/model/a2ui_message.dart
Lines 214 to 221 in ae53cc5
and
toJson()drops the key with'value': ?value. The wire-level distinction is lost as soon as the message hits the model.genui/packages/genui/lib/src/model/a2ui_message.dart
Lines 235 to 241 in ae53cc5
Expressed in a code example:
A deeper problem is that the data model also can't distinguish between "set to null" and "remove". There's no method to set a key to
null. Source:So even if
UpdateDataModel.fromJsonparsed the distinction correctly, the data model can't set a field tonull:genui/packages/genui/lib/src/model/data_model.dart
Lines 446 to 454 in 9a748ef
I think the most practical impact of this issue would manifest in UIs that use tri-state fields (e.g.
false,true, unset/reset). Not very common, but worth fixing in my opinon. To fix, I suggest splitting the setter API into a setter and a remover, then haveMessageProcessorbranch onhasValue.