diff --git a/rewrite-json/src/main/java/org/openrewrite/json/AddKeyValue.java b/rewrite-json/src/main/java/org/openrewrite/json/AddKeyValue.java new file mode 100644 index 00000000000..8c58c745d30 --- /dev/null +++ b/rewrite-json/src/main/java/org/openrewrite/json/AddKeyValue.java @@ -0,0 +1,142 @@ +/* + * Copyright 2024 the original author or authors. + *
+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *
+ * https://www.apache.org/licenses/LICENSE-2.0 + *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.json;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.internal.ListUtils;
+import org.openrewrite.json.tree.Json;
+import org.openrewrite.json.tree.JsonKey;
+import org.openrewrite.json.tree.JsonRightPadded;
+import org.openrewrite.json.tree.Space;
+import org.openrewrite.marker.Markers;
+
+import java.util.Collections;
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+import static org.openrewrite.Tree.randomId;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class AddKeyValue extends Recipe {
+
+ @Option(displayName = "Key path",
+ description = "A JsonPath expression to locate the *parent* JSON entry.",
+ example = "'$.subjects.*' or '$.' or '$.x[1].y.*' etc.")
+ String keyPath;
+
+ @Option(displayName = "Key",
+ description = "The key to create.",
+ example = "myKey")
+ String key;
+
+ @Option(displayName = "Value",
+ description = "The value to add to the array at the specified key. Can be of any type." +
+ " String values should be quoted to be inserted as Strings.",
+ example = "`\"myValue\"` or `{\"a\": 1}` or `[ 123 ]`")
+ String value;
+
+ @Option(displayName = "Prepend",
+ required = false,
+ description = "If set to `true` the value will be added to the beginning of the object")
+ boolean prepend;
+
+ @Override
+ public String getDisplayName() {
+ return "Add value to JSON Object";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Adds a `value` at the specified `keyPath` with the specified `key`, if the key doesn't already exist.";
+ }
+
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new JsonIsoVisitor
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.json;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.json.Assertions.json;
+
+class AddKeyValueTest implements RewriteTest {
+
+ @Test
+ @DocumentExample
+ void shouldAppendSimpleValue() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue("$.", "key", "\"val\"", false)),
+ //language=json
+ json(
+ """
+ {
+ "x": "x",
+ "l": [
+ 1, 2
+ ]
+ }
+ """,
+ """
+ {
+ "x": "x",
+ "l": [
+ 1, 2
+ ],
+ "key": "val"
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldAppendToNestedObject() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue("$.x.y.*", "key", "\"val\"", false)),
+ //language=json
+ json(
+ """
+ {
+ "x": {
+ "y": { }
+ }
+ }
+ """,
+ """
+ {
+ "x": {
+ "y": {"key": "val"}
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldAppendToNestedObjectInArray() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue("$.x[1].y.*", "key", "\"val\"", false)),
+ //language=json
+ json(
+ """
+ {
+ "x": [
+ { },
+ {
+ "y" : { }
+ }
+ ]
+ }
+ """,
+ """
+ {
+ "x": [
+ { },
+ {
+ "y" : {"key": "val"}
+ }
+ ]
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldNotAppendIfExists() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue("$.", "key", "\"val\"", false)),
+ //language=json
+ json(
+ """
+ {
+ "key": "x"
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldAppendObject() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue(
+ "$.", "key", """
+ { "a": "b" }
+ """.trim(), false)),
+ //language=json
+ json(
+ """
+ {
+ "x": "x",
+ "l": [
+ 1, 2
+ ]
+ }
+ """,
+ """
+ {
+ "x": "x",
+ "l": [
+ 1, 2
+ ],
+ "key": { "a": "b" }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldPrependObject() {
+ rewriteRun(
+ spec -> spec.recipe(new AddKeyValue(
+ "$.", "key", """
+ { "a": "b" }
+ """.trim(), true)),
+ //language=json
+ json(
+ """
+ {
+ "x": "x",
+ "l": [
+ 1, 2
+ ]
+ }
+ """,
+ """
+ {
+ "key": { "a": "b" },
+ "x": "x",
+ "l": [
+ 1, 2
+ ]
+ }
+ """
+ )
+ );
+ }
+}