a2ui-project / a2ui-project/a2ui
`UpdateDataModel` doesn't distinguish between the value-omitted and value-null cases
- Vorherrschende Sprache
- TypeScript
- Sterne
- 16.4k
- Forks
- 1.3k
- Ø Merge
- 2 T. 13 Std.
- Gemergte PRs (30 T.)
- 134
Beschreibung
_↴ Ported from [flutter/genui#935](https://github.com/flutter/genui/issues/935) — originally opened by [andrewkolos](https://github.com/andrewkolos) on 2026-05-27._
_Original labels: front-line-handled_
---
The v0.9 spec [distinguishes two intents on `updateDataModel`:](https://github.com/google/A2UI/blob/0e0375749bf8a940d57969d612e74f7221ad246e/specification/v0_9/json/server_to_client.json#L98)
- `"value": ` (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:
https://github.com/flutter/genui/blob/ae53cc52b35b72312f01b80eeb565163e3c869d7/packages/genui/lib/src/model/a2ui_message.dart#L214-L221
and `toJson()` drops the key with `'value': ?value`. The wire-level distinction is lost as soon as the message hits the model.
https://github.com/flutter/genui/blob/ae53cc52b35b72312f01b80eeb565163e3c869d7/packages/genui/lib/src/model/a2ui_message.dart#L235-L241
Expressed in a code example:
```dart
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:
```dart
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`:
https://github.com/flutter/genui/blob/9a748ef7c71689421e1f57bace12a734192351fa/packages/genui/lib/src/model/data_model.dart#L446-L454
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`.
---
### 1 comment(s) from the original issue
**[andrewkolos](https://github.com/andrewkolos)** commented on 2026-06-24:
Putting this down for now and putting on it my personal backlog. I will reassign myself if I begin looking at it again.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.