Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,55 @@ System.out.println("Avg price: " + priceStats.getAverage());

See `json-java21-jsonpath/README.md` for JsonPath operators and more examples.

## JSON Transforms

This repo also includes a JSON Transforms implementation (module `json-java21-transforms`) based on the Microsoft JSON Document Transforms specification:
https://github.com/Microsoft/json-document-transforms/wiki

JSON Transforms provides a declarative way to transform JSON documents using transform specifications. A transform specification is itself a JSON document that describes operations (rename, remove, replace, merge) to apply to a source document.

```java
import jdk.sandbox.java.util.json.*;
import json.java21.transforms.JsonTransforms;

// Source document
JsonValue source = Json.parse("""
{
"name": "Alice",
"age": 30,
"city": "Seattle"
}
""");

// Transform specification
JsonValue transform = Json.parse("""
{
"name": {
"@jdt.rename": "fullName"
},
"age": {
"@jdt.remove": true
},
"country": {
"@jdt.value": "USA"
}
}
""");

// Parse and apply transform
JsonTransforms transformer = JsonTransforms.parse(transform);
JsonValue result = transformer.apply(source);

// Result:
// {
// "fullName": "Alice",
// "city": "Seattle",
// "country": "USA"
// }
```

See `json-java21-transforms/README.md` for supported operations and more examples.

## Contributing

If you use an AI assistant while contributing, ensure it follows the contributor/agent workflow rules in `AGENTS.md`.
Expand Down
36 changes: 36 additions & 0 deletions json-java21-transforms/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# json-java21-transforms/AGENTS.md

This file is for contributor/agent operational notes. Read `json-java21-transforms/README.md` for purpose, supported operations, and user-facing examples.

- User docs MUST recommend only `./mvnw`.
- The `$(command -v mvnd || command -v mvn || command -v ./mvnw)` wrapper is for local developer speed only; do not put it in user-facing docs.

## Specification

This module implements JSON Document Transforms based on the Microsoft specification:
- Wiki: https://github.com/Microsoft/json-document-transforms/wiki
- C# Implementation: https://github.com/Microsoft/json-document-transforms

**IMPORTANT**: Do NOT call this technology "JDT" - that abbreviation conflicts with RFC 8927 (JSON Type Definition) which is implemented in `json-java21-jtd`. Always refer to this as "json-transforms" or "JSON Transforms".

## Stable Code Entry Points

- `json-java21-transforms/src/main/java/json/java21/transforms/JsonTransforms.java` - Main API
- `json-java21-transforms/src/main/java/json/java21/transforms/JsonTransformsParser.java` - Parser
- `json-java21-transforms/src/main/java/json/java21/transforms/JsonTransformsAst.java` - AST types
- `json-java21-transforms/src/main/java/json/java21/transforms/JsonTransformsParseException.java` - Parse errors

## When Changing Syntax/Behavior

- Update `JsonTransformsAst` + `JsonTransformsParser` + `JsonTransforms` together.
- Add parser + evaluation tests; new tests should extend `JsonTransformsLoggingConfig`.

## Design Principles

- Follow the parse/apply two-phase pattern like `JsonPath` and `Jtd`
- Use immutable records for AST nodes
- Pre-compile JsonPath expressions during parse phase
- Apply transforms using stack-based evaluation (no recursion)
- Defensive copies in all record constructors

Consider these rules if they affect your changes.
198 changes: 198 additions & 0 deletions json-java21-transforms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# JSON Transforms

A Java implementation of JSON document transforms based on the Microsoft JSON Document Transforms specification.

**Specification**: https://github.com/Microsoft/json-document-transforms/wiki

**Reference Implementation (C#)**: https://github.com/Microsoft/json-document-transforms

## Overview

JSON Transforms provides a declarative way to transform JSON documents using transform specifications. A transform specification is itself a JSON document that describes operations (rename, remove, replace, merge) to apply to a source document.

This implementation uses JsonPath queries (from `json-java21-jsonpath`) to select target nodes in the source document.

## Quick Start

```java
import jdk.sandbox.java.util.json.*;
import json.java21.transforms.JsonTransforms;

// Source document
JsonValue source = Json.parse("""
{
"name": "Alice",
"age": 30,
"city": "Seattle"
}
""");

// Transform specification
JsonValue transform = Json.parse("""
{
"name": {
"@jdt.rename": "fullName"
},
"age": {
"@jdt.remove": true
},
"country": {
"@jdt.value": "USA"
}
}
""");

// Parse and apply transform
JsonTransforms transformer = JsonTransforms.parse(transform);
JsonValue result = transformer.apply(source);

// Result:
// {
// "fullName": "Alice",
// "city": "Seattle",
// "country": "USA"
// }
```

## Transform Operations

### @jdt.path

Specifies a JsonPath query to select which elements to transform. When used at the root of a transform, applies the transform to matching elements.

```json
{
"@jdt.path": "$.users[*]",
"status": {
"@jdt.value": "active"
}
}
```

### @jdt.value

Sets the value of a property. The value can be any JSON type (string, number, boolean, null, object, array).

```json
{
"newProperty": {
"@jdt.value": "hello"
},
"count": {
"@jdt.value": 42
}
}
```

### @jdt.remove

Removes a property from the document. Set to `true` to remove.

```json
{
"obsoleteField": {
"@jdt.remove": true
}
}
```

### @jdt.rename

Renames a property to a new name.

```json
{
"oldName": {
"@jdt.rename": "newName"
}
}
```

### @jdt.replace

Replaces a property value with a new value. Unlike `@jdt.value`, this only works if the property already exists.

```json
{
"existingField": {
"@jdt.replace": "new value"
}
}
```

### @jdt.merge

Performs a deep merge of an object with the existing value.

```json
{
"config": {
"@jdt.merge": {
"newSetting": true,
"timeout": 5000
}
}
}
```

## Design

JSON Transforms follows the two-phase pattern used by other modules in this repository:

1. **Parse Phase**: The transform specification is parsed into an immutable AST (Abstract Syntax Tree) of transform operations. JsonPath expressions are pre-compiled for efficiency.

2. **Apply Phase**: The parsed transform is applied to source documents. The same parsed transform can be reused across multiple source documents.

### Architecture

- **Immutable Records**: All transform operations are represented as immutable records
- **Stack-based Evaluation**: Transforms are applied using a stack-based approach to avoid stack overflow on deeply nested documents
- **JsonPath Integration**: Uses the `json-java21-jsonpath` module for powerful node selection

## Building and Testing

```bash
# Build the module
./mvnw compile -pl json-java21-transforms -am

# Run tests
./mvnw test -pl json-java21-transforms -am

# Run with detailed logging
./mvnw test -pl json-java21-transforms -am -Djava.util.logging.ConsoleHandler.level=FINE
```

## API Reference

### JsonTransforms

Main entry point for parsing and applying transforms.

```java
// Parse a transform specification
JsonTransforms transform = JsonTransforms.parse(transformJson);

// Apply to a source document
JsonValue result = transform.apply(sourceJson);
```

### JsonTransformsAst

The AST (Abstract Syntax Tree) representation of transform operations. This is a sealed interface hierarchy:

- `TransformRoot` - Root of a transform specification
- `PathTransform` - Transform with JsonPath selector (`@jdt.path`)
- `ValueOp` - Set value operation (`@jdt.value`)
- `RemoveOp` - Remove operation (`@jdt.remove`)
- `RenameOp` - Rename operation (`@jdt.rename`)
- `ReplaceOp` - Replace operation (`@jdt.replace`)
- `MergeOp` - Merge operation (`@jdt.merge`)
- `NestedTransform` - Nested transform for object properties

### JsonTransformsParseException

Thrown when a transform specification is invalid.

## License

This project is part of the OpenJDK JSON API implementation and follows the same licensing terms.
90 changes: 90 additions & 0 deletions json-java21-transforms/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.simbo1905.json</groupId>
<artifactId>parent</artifactId>
<version>0.1.9</version>
</parent>

<artifactId>java.util.json.transforms</artifactId>
<packaging>jar</packaging>
<name>java.util.json Java21 Backport JSON Transforms</name>
<url>https://simbo1905.github.io/java.util.json.Java21/</url>
<scm>
<connection>scm:git:https://github.com/simbo1905/java.util.json.Java21.git</connection>
<developerConnection>scm:git:git@github.com:simbo1905/java.util.json.Java21.git</developerConnection>
<url>https://github.com/simbo1905/java.util.json.Java21</url>
<tag>HEAD</tag>
</scm>
<description>JSON Transforms implementation for the java.util.json Java 21 backport; parses transform specifications and applies them to JSON documents.</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
</properties>

<dependencies>
<dependency>
<groupId>io.github.simbo1905.json</groupId>
<artifactId>java.util.json</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.simbo1905.json</groupId>
<artifactId>java.util.json.jsonpath</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Treat all warnings as errors, enable all lint warnings -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
<arg>-Xdiags:verbose</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-ea</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading
Loading