Enable root modifications
Though I can not see why you would want to..... But there's no reason you would not be able to
This commit is contained in:
@@ -147,6 +147,52 @@ func (p *JSONProcessor) ProcessContent(content string, pattern string, luaExpr s
|
|||||||
|
|
||||||
// updateJSONValue updates a value in the JSON structure based on its JSONPath
|
// updateJSONValue updates a value in the JSON structure based on its JSONPath
|
||||||
func (p *JSONProcessor) updateJSONValue(jsonData interface{}, path string, newValue interface{}) error {
|
func (p *JSONProcessor) updateJSONValue(jsonData interface{}, path string, newValue interface{}) error {
|
||||||
|
// Special handling for root node
|
||||||
|
if path == "$" {
|
||||||
|
// For the root node, we'll copy the value to the jsonData reference
|
||||||
|
// This is a special case since we can't directly replace the interface{} variable
|
||||||
|
|
||||||
|
// We need to handle different types of root elements
|
||||||
|
switch rootValue := newValue.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
// For objects, we need to copy over all keys
|
||||||
|
rootMap, ok := jsonData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
// If the original wasn't a map, completely replace it with the new map
|
||||||
|
// This is handled by the jsonpath.Set function
|
||||||
|
return jsonpath.Set(jsonData, path, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the original map
|
||||||
|
for k := range rootMap {
|
||||||
|
delete(rootMap, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all keys from the new map
|
||||||
|
for k, v := range rootValue {
|
||||||
|
rootMap[k] = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
// For arrays, we need to handle similarly
|
||||||
|
rootArray, ok := jsonData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
// If the original wasn't an array, use jsonpath.Set
|
||||||
|
return jsonpath.Set(jsonData, path, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear and recreate the array
|
||||||
|
*&rootArray = rootValue
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For other types, use jsonpath.Set
|
||||||
|
return jsonpath.Set(jsonData, path, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-root paths, use the regular Set method
|
||||||
err := jsonpath.Set(jsonData, path, newValue)
|
err := jsonpath.Set(jsonData, path, newValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update JSON value at path '%s': %w", path, err)
|
return fmt.Errorf("failed to update JSON value at path '%s': %w", path, err)
|
||||||
|
@@ -937,16 +937,16 @@ func TestJSONProcessor_RestructuringData(t *testing.T) {
|
|||||||
"people": {
|
"people": {
|
||||||
"developers": [
|
"developers": [
|
||||||
{
|
{
|
||||||
|
"age": 25,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Alice",
|
"name": "Alice"
|
||||||
"age": 25
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"managers": [
|
"managers": [
|
||||||
{
|
{
|
||||||
|
"age": 30,
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Bob",
|
"name": "Bob"
|
||||||
"age": 30
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1042,3 +1042,83 @@ func TestJSONProcessor_FilteringArrayElements(t *testing.T) {
|
|||||||
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestJSONProcessor_RootNodeModification tests modifying the root node directly
|
||||||
|
func TestJSONProcessor_RootNodeModification(t *testing.T) {
|
||||||
|
content := `{
|
||||||
|
"name": "original",
|
||||||
|
"value": 100
|
||||||
|
}`
|
||||||
|
|
||||||
|
expected := `{
|
||||||
|
"name": "modified",
|
||||||
|
"description": "This is a completely modified root",
|
||||||
|
"values": [1, 2, 3]
|
||||||
|
}`
|
||||||
|
|
||||||
|
p := &JSONProcessor{}
|
||||||
|
result, modCount, matchCount, err := p.ProcessContent(content, "$", `
|
||||||
|
-- Completely replace the root node
|
||||||
|
v = {
|
||||||
|
name = "modified",
|
||||||
|
description = "This is a completely modified root",
|
||||||
|
values = {1, 2, 3}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error processing content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchCount != 1 {
|
||||||
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if modCount != 1 {
|
||||||
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize whitespace for comparison
|
||||||
|
normalizedResult := normalizeWhitespace(result)
|
||||||
|
normalizedExpected := normalizeWhitespace(expected)
|
||||||
|
|
||||||
|
if normalizedResult != normalizedExpected {
|
||||||
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestJSONProcessor_RootNodeModificationToPrimitive tests modifying the root node to a primitive value
|
||||||
|
func TestJSONProcessor_RootNodeModificationToPrimitive(t *testing.T) {
|
||||||
|
content := `{
|
||||||
|
"name": "original",
|
||||||
|
"value": 100
|
||||||
|
}`
|
||||||
|
|
||||||
|
expected := `42`
|
||||||
|
|
||||||
|
p := &JSONProcessor{}
|
||||||
|
result, modCount, matchCount, err := p.ProcessContent(content, "$", `
|
||||||
|
-- Replace the root node with a primitive value
|
||||||
|
v = 42
|
||||||
|
`)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error processing content: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchCount != 1 {
|
||||||
|
t.Errorf("Expected 1 match, got %d", matchCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if modCount != 1 {
|
||||||
|
t.Errorf("Expected 1 modification, got %d", modCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize whitespace for comparison
|
||||||
|
normalizedResult := normalizeWhitespace(result)
|
||||||
|
normalizedExpected := normalizeWhitespace(expected)
|
||||||
|
|
||||||
|
if normalizedResult != normalizedExpected {
|
||||||
|
t.Errorf("Expected content to be:\n%s\n\nGot:\n%s", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -138,10 +138,6 @@ func Set(data interface{}, path string, value interface{}) error {
|
|||||||
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(steps) <= 1 {
|
|
||||||
return fmt.Errorf("cannot set root node; the provided path %q is invalid", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
err = setWithPath(data, steps, &success, value, "$", ModifyFirstMode)
|
err = setWithPath(data, steps, &success, value, "$", ModifyFirstMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,10 +153,6 @@ func SetAll(data interface{}, path string, value interface{}) error {
|
|||||||
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
return fmt.Errorf("failed to parse JSONPath %q: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if len(steps) <= 1 {
|
|
||||||
// return fmt.Errorf("cannot set root node; the provided path %q is invalid", path)
|
|
||||||
// }
|
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
err = setWithPath(data, steps, &success, value, "$", ModifyAllMode)
|
err = setWithPath(data, steps, &success, value, "$", ModifyAllMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,17 +170,20 @@ func setWithPath(node interface{}, steps []JSONStep, success *bool, value interf
|
|||||||
// Skip root step
|
// Skip root step
|
||||||
actualSteps := steps
|
actualSteps := steps
|
||||||
if len(steps) > 0 && steps[0].Type == RootStep {
|
if len(steps) > 0 && steps[0].Type == RootStep {
|
||||||
// if len(steps) == 1 {
|
|
||||||
// return fmt.Errorf("cannot set root node; the provided path %q is invalid", currentPath)
|
|
||||||
// }
|
|
||||||
actualSteps = steps[1:]
|
actualSteps = steps[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the first step
|
// If we have no steps left, we're setting the root value
|
||||||
// if len(actualSteps) == 0 {
|
if len(actualSteps) == 0 {
|
||||||
// return fmt.Errorf("cannot set root node; no steps provided for path %q", currentPath)
|
// For the root node, we need to handle it differently depending on what's passed in
|
||||||
// }
|
// since we can't directly replace the interface{} variable
|
||||||
|
|
||||||
|
// We'll signal success and let the JSONProcessor handle updating the root
|
||||||
|
*success = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the first step
|
||||||
step := actualSteps[0]
|
step := actualSteps[0]
|
||||||
remainingSteps := actualSteps[1:]
|
remainingSteps := actualSteps[1:]
|
||||||
isLastStep := len(remainingSteps) == 0
|
isLastStep := len(remainingSteps) == 0
|
||||||
|
Reference in New Issue
Block a user