Skip to content

Docs: no guidance on serializing MetaObjectAware / PojoObject instances — consumers hand-roll Jackson workarounds #273

Description

@dmealing

Problem

The Java runtime ships a metadata-driven JSON layer for MetaObject-backed instances, but none of the generated agent guidance mentions it exists. A consumer (or a coding agent following the shipped skills) hits a serialization failure on a generated VO, finds no guidance, and hand-rolls a Jackson workaround — reimplementing, badly, something the jar already provides.

That happened to us this week, twice, on the same codebase.

What exists but is undocumented

Present in metaobjects-metadata 7.20.11 and metaobjects-codegen-base 7.20.11:

class what it is
com.metaobjects.io.object.json.JsonObjectWriter / JsonObjectReader metadata-driven object JSON read/write
com.metaobjects.io.object.gson.MetaObjectSerializer Gson serializer for MetaObject-backed instances
com.metaobjects.object.MetaObjectAware the interface that makes an instance metadata-aware
com.metaobjects.generator.direct.object.javacode.PojoAwareCodeWriter codegen emitting plain POJOs that implement MetaObjectAware

The documentation gap

Across the generated agent context for a java, kotlin server, react, tanstack project:

  • .metaobjects/AGENTS.md and .metaobjects/CLAUDE.md0 mentions of any of the above
  • every metaobjects-* skill, including metaobjects-codegen/references/java.md and metaobjects-runtime-ui/references/java.md0 mentions

metaobjects-codegen/references/java.md has a generator table listing SpringControllerGenerator, SpringDtoGenerator, SpringRepositoryGenerator, SpringPayloadGenerator, SpringOutputParserGenerator, SpringFilterAllowlistGenerator — but not PojoAwareCodeWriter, and nothing anywhere says how a generated object should be serialized.

Why the gap bites specifically

Extract-route VOs extend PojoObject, whose framework field is a MetaObject back-reference. A default Jackson ObjectMapper walking that reference fails with InaccessibleObjectException under JPMS. The failure surfaces at the consumer's serialization call, far from any metadata concept, so the natural (wrong) reaction is to treat it as a Jackson problem:

  • Two of our services shipped with the response-VO capture disabled entirely, each carrying a comment saying the parsed views "carry MetaObjects framework internals" and could not be serialized — i.e. a real feature was switched off because the supported path was undiscoverable.
  • The eventual "fix" was a hand-written @JsonIgnoreType mixin on MetaObject plus FIELD visibility and FAIL_ON_EMPTY_BEANS=false. It works, and it should never have been written.

Both are consumer-side symptoms of one missing paragraph.

Suggested fix

  1. A serialization section in references/java.md (and the equivalent per-port fragment): when you have a MetaObject-backed instance and need JSON, use JsonObjectWriter/MetaObjectSerializer — with a snippet. State explicitly that a default Jackson/Gson mapper over a PojoObject subtype will fail on the MetaObject back-reference, and that this is expected rather than a bug to work around.
  2. List PojoAwareCodeWriter in the generator table, and say when to choose POJO+MetaObjectAware output over PojoObject — a plain POJO that is metadata-aware serializes with an ordinary mapper, which is the simpler answer for consumers who mostly want normal Jackson behaviour.
  3. One line in AGENTS.md under the principles, in the same spirit as "use the generated constants for any string that names metadata": serialize generated objects through the MetaObjects JSON layer, don't hand-configure a mapper around the framework fields.
  4. Optionally a metaobjects-verify / adoption-audit check that flags a hand-configured ObjectMapper touching generated types.

Context

Found while adding per-call telemetry capture in a Java/Kotlin consumer on metaobjects 7.20.11. Happy to contribute the references/java.md section if the shape above looks right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions