Skip to content

WW-3364 Use registered type converters in JSON serialization (opt-in) - #2002

Merged
lukaszlenart merged 2 commits into
mainfrom
WW-3364-json-writer-type-converters
Oct 10, 2026
Merged

lukaszlenart merged 2 commits into
mainfrom
WW-3364-json-writer-type-converters

Conversation

@lukaszlenart

@lukaszlenart lukaszlenart commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Fixes WW-3364

Summary

The JSON plugin never consulted the type converters an application registers, so a value type rendered through a converter in the HTML view (e.g. Money → "12.50 EUR") came out as a nested bean in the JSON response.

This adds an opt-in constant, struts.json.writer.useTypeConverters (default false). When enabled, StrutsJSONWriter serializes a value that is not a built-in JSON type with the converter registered for its class — or a superclass/interface, via XWorkConverter.lookup() — and writes the result as a JSON string.

<constant name="struts.json.writer.useTypeConverters" value="true"/>

Behaviour

  • Off by default — existing output is unchanged.
  • Built-in types are never converted. The check sits after the Boolean/Number/String/Map/Iterable/Date/Temporal/Locale/Enum branches, so @JSON(format) and struts.json.dateformat keep working. It runs before the record branch, so a record value type can have a converter.
  • Only per-type (application-wide) converters (struts-conversion.properties / xwork-conversion.properties, @TypeConversion(type = ConversionType.APPLICATION)) are used; per-property Foo-conversion.properties mappings need context the writer does not have.
  • java.io.File is skipped, so the framework's own UploadedFileConverter is never applied.
  • @JSONFieldBridge keeps precedence — it is applied at the property level before the value is written.
  • Failure falls back — if a converter throws or returns a non-String, the value is serialized as a bean/record as before (logged at debug).
  • A converted value is written as a single string: nested includeProperties/excludeProperties patterns and @JSON annotations on its members no longer apply to it. This will be stated in the JSON plugin docs.

Out of scope: using converters on the read side (JSONPopulator).

Test plan

  • New StrutsJSONWriterTypeConverterTest (container-backed, StrutsTestCase): default off, converted value, nested property, interface-registered converter, record, no converter, field-bridge precedence, failing converter fallback, built-in types untouched, File skipped
  • Each guard test was mutation-checked: removing the flag check, the exception fallback, the File exclusion, the built-in ordering, or the field bridge makes the corresponding test fail
  • ./mvnw test -DskipAssembly -pl plugins/json — 177 tests, 0 failures

Docs: a paragraph for the JSON plugin page follows in apache/struts-site.

🤖 Generated with Claude Code

lukaszlenart and others added 2 commits October 10, 2026 12:31
Adds the opt-in struts.json.writer.useTypeConverters constant (default
false). When enabled, StrutsJSONWriter serializes a value that is not a
built-in JSON type with the type converter registered for its class (or a
superclass/interface) and writes the result as a JSON string, falling back
to bean/record serialization when no converter is registered or the
converter fails. @JSONFieldBridge keeps precedence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plexity

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@lukaszlenart
lukaszlenart merged commit 8503fb7 into main Oct 10, 2026
16 checks passed
@lukaszlenart
lukaszlenart deleted the WW-3364-json-writer-type-converters branch October 10, 2026 10:49
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.

1 participant