Skip to content

💡 Suggestion: small integration points for SJF4J (JSON-oriented handling) #1108

@hannyu

Description

@hannyu

Hi, I’m the author of SJF4J (Simple JSON Facade for Java).

After reading adk-java, I think SJF4J could be useful in a few narrow, concrete places.

This is not about replacing Jackson, but about improving places where ADK already operates on mutable nested Map/List/value graphs.


1. State / stateDelta

State and EventActions.stateDelta() are already very close to a JSON-like object model:

  • Map<String, Object> state
  • nested Map/List values
  • a separate delta
  • custom handling for removal

Relevant code:

  • sessions/State.java
  • events/EventActions.java
  • sessions/InMemorySessionService.java

Today:

  • deep updates rely on manual merge logic
  • deletions use a sentinel
  • changes are tracked as a flat map

For nested state, this becomes harder to reason about and replay.

A patch-based approach makes the transition explicit:

Map<String, Object> previous = oldSession.state();
Map<String, Object> next = newState;

JsonPatch patch = JsonPatch.diff(previous, next);
patch.apply(session.state());

This would:

  • remove the need for custom deep-merge logic (already present in EventActions.Builder.merge())
  • avoid sentinel-based delete semantics
  • make state transitions explicit and replayable

2. SchemaUtils

SchemaUtils currently does lightweight recursive validation (required + basic type checks).

This works, but it cannot express common constraints:

Map<String, Object> args = Map.of(
    "city", "",
    "days", -1
);

This is type-correct but still semantically invalid.

With JSON Schema:

{
  "type": "object",
  "properties": {
    "city": { "type": "string", "minLength": 1 },
    "days": { "type": "integer", "minimum": 1 }
  },
  "required": ["city", "days"]
}

SJF4J supports:

  • minLength, minimum, enum
  • oneOf / polymorphic inputs
  • additionalProperties
  • full Draft 2020-12 validation

This could strengthen validation for:

  • tool args
  • structured outputs
  • configs
  • session payloads

3. zjsonpatch usage

In LlmRequestComparator, the current flow is roughly:

object → JSON string → JsonNode → diff

Using a native object-graph diff avoids that round trip:

JsonPatch patch = JsonPatch.diff(recorded, current);

Potential benefits:

  • no intermediate JsonNode
  • one less dependency
  • works directly on existing Map/List structures

4. YAML handling

There are multiple YAML-specific paths today:

  • ConfigAgentUtils
  • YamlPreprocessor
  • RecordingsLoader

SJF4J can unify YAML and JSON into the same structural model:

@NodeNaming(NamingStrategy.SNAKE_CASE)
public class RootAgentConfig extends JsonObject {}

RootAgentConfig config = Sjf4j.fromYaml(yamlText, RootAgentConfig.class);
new SchemaValidator().requireValid(config);

This allows:

  • consistent handling of YAML / JSON / in-memory state
  • shared path access / patch / validation
  • simpler snake_case mapping

🎯 Summary

Potential integration points:

  • State → patch-based state transitions
  • SchemaUtils → full JSON Schema validation
  • zjsonpatch → direct object diff
  • YAML → unified structural model

All of these are local improvements, not architectural changes.


🙏

SJF4J is still evolving and not yet as mature as long-established libraries.
I’ll continue improving it and keeping it stable.

If any of these directions are interesting, I’d be happy to help and follow up on this.

Thanks again for the great project 👍

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