|
| 1 | +# JSON Transforms |
| 2 | + |
| 3 | +A Java implementation of JSON document transforms based on the Microsoft JSON Document Transforms specification. |
| 4 | + |
| 5 | +**Specification**: https://github.com/Microsoft/json-document-transforms/wiki |
| 6 | + |
| 7 | +**Reference Implementation (C#)**: https://github.com/Microsoft/json-document-transforms |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +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. |
| 12 | + |
| 13 | +This implementation uses JsonPath queries (from `json-java21-jsonpath`) to select target nodes in the source document. |
| 14 | + |
| 15 | +## Quick Start |
| 16 | + |
| 17 | +```java |
| 18 | +import jdk.sandbox.java.util.json.*; |
| 19 | +import json.java21.transforms.JsonTransforms; |
| 20 | + |
| 21 | +// Source document |
| 22 | +JsonValue source = Json.parse(""" |
| 23 | + { |
| 24 | + "name": "Alice", |
| 25 | + "age": 30, |
| 26 | + "city": "Seattle" |
| 27 | + } |
| 28 | + """); |
| 29 | + |
| 30 | +// Transform specification |
| 31 | +JsonValue transform = Json.parse(""" |
| 32 | + { |
| 33 | + "name": { |
| 34 | + "@jdt.rename": "fullName" |
| 35 | + }, |
| 36 | + "age": { |
| 37 | + "@jdt.remove": true |
| 38 | + }, |
| 39 | + "country": { |
| 40 | + "@jdt.value": "USA" |
| 41 | + } |
| 42 | + } |
| 43 | + """); |
| 44 | + |
| 45 | +// Parse and apply transform |
| 46 | +JsonTransforms transformer = JsonTransforms.parse(transform); |
| 47 | +JsonValue result = transformer.apply(source); |
| 48 | + |
| 49 | +// Result: |
| 50 | +// { |
| 51 | +// "fullName": "Alice", |
| 52 | +// "city": "Seattle", |
| 53 | +// "country": "USA" |
| 54 | +// } |
| 55 | +``` |
| 56 | + |
| 57 | +## Transform Operations |
| 58 | + |
| 59 | +### @jdt.path |
| 60 | + |
| 61 | +Specifies a JsonPath query to select which elements to transform. When used at the root of a transform, applies the transform to matching elements. |
| 62 | + |
| 63 | +```json |
| 64 | +{ |
| 65 | + "@jdt.path": "$.users[*]", |
| 66 | + "status": { |
| 67 | + "@jdt.value": "active" |
| 68 | + } |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +### @jdt.value |
| 73 | + |
| 74 | +Sets the value of a property. The value can be any JSON type (string, number, boolean, null, object, array). |
| 75 | + |
| 76 | +```json |
| 77 | +{ |
| 78 | + "newProperty": { |
| 79 | + "@jdt.value": "hello" |
| 80 | + }, |
| 81 | + "count": { |
| 82 | + "@jdt.value": 42 |
| 83 | + } |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +### @jdt.remove |
| 88 | + |
| 89 | +Removes a property from the document. Set to `true` to remove. |
| 90 | + |
| 91 | +```json |
| 92 | +{ |
| 93 | + "obsoleteField": { |
| 94 | + "@jdt.remove": true |
| 95 | + } |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +### @jdt.rename |
| 100 | + |
| 101 | +Renames a property to a new name. |
| 102 | + |
| 103 | +```json |
| 104 | +{ |
| 105 | + "oldName": { |
| 106 | + "@jdt.rename": "newName" |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +### @jdt.replace |
| 112 | + |
| 113 | +Replaces a property value with a new value. Unlike `@jdt.value`, this only works if the property already exists. |
| 114 | + |
| 115 | +```json |
| 116 | +{ |
| 117 | + "existingField": { |
| 118 | + "@jdt.replace": "new value" |
| 119 | + } |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +### @jdt.merge |
| 124 | + |
| 125 | +Performs a deep merge of an object with the existing value. |
| 126 | + |
| 127 | +```json |
| 128 | +{ |
| 129 | + "config": { |
| 130 | + "@jdt.merge": { |
| 131 | + "newSetting": true, |
| 132 | + "timeout": 5000 |
| 133 | + } |
| 134 | + } |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +## Design |
| 139 | + |
| 140 | +JSON Transforms follows the two-phase pattern used by other modules in this repository: |
| 141 | + |
| 142 | +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. |
| 143 | + |
| 144 | +2. **Apply Phase**: The parsed transform is applied to source documents. The same parsed transform can be reused across multiple source documents. |
| 145 | + |
| 146 | +### Architecture |
| 147 | + |
| 148 | +- **Immutable Records**: All transform operations are represented as immutable records |
| 149 | +- **Stack-based Evaluation**: Transforms are applied using a stack-based approach to avoid stack overflow on deeply nested documents |
| 150 | +- **JsonPath Integration**: Uses the `json-java21-jsonpath` module for powerful node selection |
| 151 | + |
| 152 | +## Building and Testing |
| 153 | + |
| 154 | +```bash |
| 155 | +# Build the module |
| 156 | +./mvnw compile -pl json-java21-transforms -am |
| 157 | + |
| 158 | +# Run tests |
| 159 | +./mvnw test -pl json-java21-transforms -am |
| 160 | + |
| 161 | +# Run with detailed logging |
| 162 | +./mvnw test -pl json-java21-transforms -am -Djava.util.logging.ConsoleHandler.level=FINE |
| 163 | +``` |
| 164 | + |
| 165 | +## API Reference |
| 166 | + |
| 167 | +### JsonTransforms |
| 168 | + |
| 169 | +Main entry point for parsing and applying transforms. |
| 170 | + |
| 171 | +```java |
| 172 | +// Parse a transform specification |
| 173 | +JsonTransforms transform = JsonTransforms.parse(transformJson); |
| 174 | + |
| 175 | +// Apply to a source document |
| 176 | +JsonValue result = transform.apply(sourceJson); |
| 177 | +``` |
| 178 | + |
| 179 | +### JsonTransformsAst |
| 180 | + |
| 181 | +The AST (Abstract Syntax Tree) representation of transform operations. This is a sealed interface hierarchy: |
| 182 | + |
| 183 | +- `TransformRoot` - Root of a transform specification |
| 184 | +- `PathTransform` - Transform with JsonPath selector (`@jdt.path`) |
| 185 | +- `ValueOp` - Set value operation (`@jdt.value`) |
| 186 | +- `RemoveOp` - Remove operation (`@jdt.remove`) |
| 187 | +- `RenameOp` - Rename operation (`@jdt.rename`) |
| 188 | +- `ReplaceOp` - Replace operation (`@jdt.replace`) |
| 189 | +- `MergeOp` - Merge operation (`@jdt.merge`) |
| 190 | +- `NestedTransform` - Nested transform for object properties |
| 191 | + |
| 192 | +### JsonTransformsParseException |
| 193 | + |
| 194 | +Thrown when a transform specification is invalid. |
| 195 | + |
| 196 | +## License |
| 197 | + |
| 198 | +This project is part of the OpenJDK JSON API implementation and follows the same licensing terms. |
0 commit comments