Skip to content

[FIX] endpoint, endpoint_json2: serialize response payloads with json_default - #34

Merged
yostashiro merged 5 commits into
19.0from
endpoint_json2
Aug 29, 2026
Merged

[FIX] endpoint, endpoint_json2: serialize response payloads with json_default#34
yostashiro merged 5 commits into
19.0from
endpoint_json2

Conversation

@smorita7749

@smorita7749 smorita7749 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

A JSON2 response crashes whenever the payload holds a value json cannot represent natively — fields.Domain (from a field computed with fields.Domain.AND, a common pattern for view-only domain widgets), but also date, datetime and bytes.

The root cause is in endpoint, not endpoint_json2: _make_json_response calls json.dumps() with no default= hook, while every other JSON response in Odoo goes through json.dumps(data, default=json_default) (odoo/http.py). endpoint_json2 had grown its own payload walker to compensate, reimplementing odoo.tools.json.json_default without knowing it existed — and converting containers without their contents, so a Domain became a list while a date inside one of its conditions stayed a date.

  • endpoint: pass json_default to json.dumps. json.dumps applies it at every depth, so nested values are handled without walking the payload. An endpoint can also supply its own hook through the result dict; it replaces the default for the whole payload and is expected to delegate to json_default for the types it does not render itself.
  • endpoint_json2: drop the walker and pass such a hook. date, bytes and Domain then need no handling of our own.

Only datetime keeps a deviation: json_default renders naive UTC, which cannot express json2_tz, so datetimes stay ISO-8601 with an explicit offset.

One behaviour change worth noting: bytes now decode strictly. Binary fields read back base64, so this is exact for real field values, where the previous errors="replace" would have silently mangled anything else.

Upstream: supersedes the endpoint_json2-only fix in OCA/web-api#135. To be relayed as two PRs, one per module.

Assisted-by: Claude Opus 5

Odoo's fields.Domain (e.g. from fields.Domain.AND) is not JSON
serializable. A field whose compute returns such an object (a common
pattern for view-only domain widgets, as added by
partner_contact_address_default in this PR) crashed the JSON2 response
when included in the payload.

Mirrors the same fix submitted upstream at OCA/web-api#135:
OCA/web-api#135
_make_json_response called json.dumps() with no default= hook, so any
payload value json cannot represent natively raised TypeError and the
request returned a 500. Every other JSON response in Odoo goes through
json.dumps(data, default=json_default) (odoo/http.py).

Pass the same hook here. It covers date, datetime, bytes, Domain, lazy
and ReadonlyDict, and json.dumps applies it at every depth, so nested
values are handled without walking the payload.

Also let an endpoint supply its own hook through the result dict, for
exec modes that need to override how a type is rendered; whatever it
does not handle falls back to Odoo's default.

Assisted-by: Claude Opus 5
_json2_serialize_values walked the whole payload to convert values json
cannot represent, reimplementing odoo.tools.json.json_default without
knowing it existed. It also converted containers without their contents,
so 7edaf54 turned a Domain into its list form but left a date inside one
of its conditions untouched.

Drop the walker and pass an encoder hook to the controller instead.
json.dumps applies it at every depth, so nested values are covered for
free, and date, bytes and Domain need no handling of our own.

Only datetime keeps a deviation: json_default renders naive UTC, which
cannot express json2_tz, so datetimes stay ISO-8601 with an explicit
offset. Note bytes now decode strictly -- Binary fields read back
base64, so this is exact for real field values, where errors="replace"
would have silently mangled anything else.

Assisted-by: Claude Opus 5
@yostashiro yostashiro changed the title [FIX] endpoint_json2: serialize Domain field values to JSON [FIX] endpoint, endpoint_json2: serialize response payloads with json_default Aug 29, 2026
Nothing asserted that a hook supplied through the result dict reaches
json.dumps, nor that _handle_exec__json2 hands its own hook over: the
json2 tests called the hook directly, so a response could fall back to
the default encoder with the suite still green.

Add a controller-level test for both branches of the hook lookup, and
one assertion on what _handle_exec__json2 returns. Drop the
serialization tests that now only exercise odoo.tools.json.json_default,
keeping the Domain case as a single assertion whose date inside a
condition still covers conversion at depth.

Assisted-by: Claude Opus 5
@yostashiro
yostashiro requested a balanced review from Copilot August 29, 2026 14:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@yostashiro

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T14:17:19.343031Z 1d937d0 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d937d04ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +44 to +45
default = kw.get("json_default") or json_default
data = json.dumps(payload, default=default)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compose custom encoders with Odoo's fallback

When an endpoint's custom hook handles only an additional type—for example, Decimal—and the same payload contains an Odoo-supported value such as a date or Domain, json.dumps invokes only the custom hook. If that hook follows the normal protocol and raises TypeError for values it does not handle, the request returns a serialization error instead of falling back to Odoo's encoder as promised. Selecting one callable with or does not chain them; wrap the custom hook so unhandled values delegate to json_default, or require the hook itself to delegate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a5193bf. The behaviour is intentional: json.dumps takes a single default= callable, and chaining would mean catching TypeError around the hook, which would also swallow genuine errors raised inside it. The contract is that a custom hook replaces the default for the whole payload and delegates to json_default for the types it does not render itself — which is what endpoint_json2._json2_json_default does. The comment (and the PR description) claimed automatic fallback and has been corrected.

The comment read as if the controller chained a custom hook with Odoo's
encoder, but json.dumps takes a single default= callable: a hook that
raises TypeError for a type it does not know would return a serialization
error rather than fall back. Say instead that the hook replaces the
default and is expected to delegate to json_default itself, which is what
endpoint_json2 does.

Assisted-by: Claude Opus 5

@yostashiro yostashiro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smorita7749 Thanks for the fix attempt!

@yostashiro
yostashiro merged commit ab05886 into 19.0 Aug 29, 2026
2 checks passed
@yostashiro
yostashiro deleted the endpoint_json2 branch August 29, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants