a2ui-project / a2ui-project/a2ui

`UpdateDataModel` doesn't distinguish between the value-omitted and value-null cases

オープン
#1,896 コメント 1 件 リアクション 0 件 担当者 1 名 @andrewkolos が担当を希望しています GitHub で見る
component: genui P2 status: first-line-handled type: bug
主要言語
TypeScript
スター
16.4k
フォーク
1.3k
平均マージ
2日 13時間
マージ済み PR(30日)
134

説明

_↴ 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.

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。