go-yaml icon indicating copy to clipboard operation
go-yaml copied to clipboard

replacing values with a multi-line string value provides broken yaml format.

Open mandelsoft opened this issue 9 months ago • 10 comments

Describe the bug

The multi line string is replaced, but the indentation of the string lines is wrong.

To Reproduce

use yaml document:

data:
  value1: orig1
  value2: orig2

replace $.data.value1 by

line1
line2

The result will be

data:
  value1: |-
  line1
  line2
  value2: orig2

instead of

data:
  value1: |-
    line1
    line2
  value2: orig2

The data is parsed with parser.ParseBytes(data, 0). The replace operation is

file, err := parser.ParseBytes(value, 0)
if err != nil {
	return err
}
p, err := yaml.PathString("$.data.value1")
if err != nil {
	return err
}
return p.ReplaceWithFile(doc, file)

Expected behavior provide correct multi-line result

Screenshots If applicable, add screenshots to help explain your problem.

Version Variables

  • Go version: 1.22
  • go-yaml's Version: v1.11.3

Additional context

The ast nodes provide some strange column values for string value. Especially there is no column info for follow-up value lines. Therefore, the String() method just uses the original string formatting from the target value, which will for sure be wrong in most cases. When replacing a value, only the column value is adapted for the new value, but this cannot be used to serialize follow-up lines.

mandelsoft avatar Apr 23 '24 09:04 mandelsoft

https://github.com/goccy/go-yaml/issues/292

Also reported two years ago without any resolution. :(

Skarlso avatar Apr 24 '24 14:04 Skarlso

I'm attempting to see if I can fix this.

Skarlso avatar Apr 24 '24 15:04 Skarlso

Reproduced the issue in path_test.go with:

		{
			path: "$.data.value1",
			dst: `
data:
  value1: orig1
  value2: orig2
`,
			src: `
line1
line2
`,
			expected: `
data:
  value1: |-
    line1
    line2
  value2: orig2
`,
		},

I'll attempt to fix the problem.

Skarlso avatar Apr 24 '24 15:04 Skarlso

The AST node replace doesn't care how deep it is so it plain does a replace on the node value resulting in a YAML that doesn't work.

Skarlso avatar Apr 24 '24 15:04 Skarlso

Tomorrow I'll try to fix this. I have an idea of tracking the deepness recursively for StringNodes for multi-line text. let's see..

Skarlso avatar Apr 24 '24 15:04 Skarlso

so I figured out that the library just expects you to pass in the value you require including the right indentation level.

		{
			path: "$.data.value1.orig1",
			dst: `
data:
  value1:
    orig1: orig1.1
  value2: orig2
`,
			src: `|-
      line1
      line2
`,
			expected: `
data:
  value1:
    orig1: |-
      line1
      line2
  value2: orig2
`,
		},

Passes as a test case.

Skarlso avatar Apr 25 '24 08:04 Skarlso

Nice find @Skarlso Can something similar be done when the new value of orig1 is an object instead of a multiline string? I ask because when I stumbled on this problem I was seeing the same problem for an object.

dee0 avatar Apr 25 '24 12:04 dee0

Do you have an example?

Skarlso avatar Apr 25 '24 12:04 Skarlso

I tried replacing objects, but the marshalling is all over the place. There is virtually no consistency in indentation whatsoever. I can't find a pattern or anything that would make it work. :(

Skarlso avatar Apr 26 '24 07:04 Skarlso

Okay, this now worked finally:

		{
			path: "$.data.value1.orig1",
			dst: `
data:
  value1:
    orig1: orig1.1
  value2: orig2
`,
			src: `
orig1:
  newValue:
    - value1
    - value2
`,
			expected: `
data:
  value1:
    orig1:
      orig1:
        newValue:
          - value1
          - value2
  value2: orig2
`,
		},

Skarlso avatar Apr 26 '24 09:04 Skarlso