Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marshalling long, single-line strings results in (correct) line breaks #166

Open
tyangliu opened this issue Mar 27, 2016 · 19 comments
Open

Comments

@tyangliu
Copy link

When long, single-line strings (i.e. does not contain "\n") are marshalled, the yaml emitter seems to force line breaks at 80 characters, but does so without prefixing the string with the multi-line notation (e.g. ">" or "|"). This results in unusable yaml output like in the example below.

Example:

data := yaml.MapSlice{{"test", "abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd"}}

res, _ := yaml.Marshal(data)
fmt.Println(string(res))

Output:

test: abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd
  abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd

As a workaround, simply appending "\n" to the end of a string that's known to be lengthy works fine, although not ideal since the yaml output now exceeds the 80 char width. Mutiline ">" prefixed outputs would be preferable.

Output with trailing \n:

test: |
  abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd aabdasfadsfdasfadsfasd abdasfadsfdasfadsfasd abdasfadsfdasfadsfasd

Appreciate if someone could confirm whether this is an issue or just incorrect usage. If a fix is needed, I'd be happy to work on a PR. Thanks!

@deitch
Copy link

deitch commented Dec 11, 2016

Been banging my head on this for the last while. Is there any way to force it not to break? It should be perfectly legitimate to say, "give me a really long line".

@wayt
Copy link

wayt commented Sep 13, 2017

I'm facing the same issue. A tag would be very nice.

@rogpeppe
Copy link
Contributor

@tyangliu

This results in unusable yaml output like in the example below.

What do you mean by "unusable yaml output" there? By my reading of the specification, that YAML looks valid. See section 8.1.3 in http://yaml.org/spec/1.2/spec.html, which says "Folding allows long lines to be broken anywhere a single space character separates two non-space characters.". That means that the example is perfectly valid YAML and the map value holds exactly the string you produced.

The Go YAML marshaler uses heuristics to try to produce decent looking output for any possible string. There are so many possible output style choices in YAML, that I'm not sure it's a good idea to start down the slippery slope of allowing string style choices to be specified with struct tags.

That said, suggestions are welcome - any such struct tag would need to be applicable to all possible strings.

@wayt
Copy link

wayt commented Sep 15, 2017

I understand this is valid according to the specification, but not compatible in all implementations (Perl for instance).
A fallback option would be appreciated.

@rogpeppe
Copy link
Contributor

I understand this is valid according to the specification, but not compatible in all implementations

How hard should go-yaml try to produce YAML that is compatible with other broken YAML implementations? As the YAML spec is insanely complex, I wouldn't be surprised if every single implementation was broken in some way.

If we decide that we want to be compatible with perl's broken implementation, the right answer I think would be to avoid breaking lines like this at all. Providing a tag so that the behaviour can be changed in individual cases doesn't seem like the right fix (what if some other field happens to have a long value with spaces in?).

tbh, if you're marshalling YAML for data interchange, I'd suggest not using YAML but using JSON instead - it's simple enough that most implrmentations get it right, and it's also compatible (mostly) with YAML.

@alexandrevicenzi
Copy link

alexandrevicenzi commented Feb 2, 2018

My guess to solve this problem is to expose YAML Encoder and Decoder class. Go encoding/json exposes its encoder and decoder, this allows setting custom parameters.

In apic.go there's a function called yaml_emitter_set_width with allows to disable long line breaks if set to -1, but this is an internal function that is never called.

Something like this code would be nice:

e := yaml.NewEncoder(wr)
e.SetLineWidth(-1)
err := e.Encode(myObj)

Using a tag would be hard I think, there's no easy way to pass the tag info to the yaml_emitter_t.

@rogpeppe
Copy link
Contributor

rogpeppe commented Feb 2, 2018

@alexandrevicenzi Now that we have an Encoder type, I think having a SetLineWidth method on it seems very reasonable. I think I'd treat any line width <= 0 as disabling line breaks. Happy to accept a PR for that.

@alexandrevicenzi
Copy link

@rogpeppe this Encoder type is only available in devel branch? If so, I'll send a PR.

@vivook
Copy link

vivook commented Feb 24, 2019

Is it any closer to fix? This is breaking my parsing yaml -> go template by marshal/unmarshal yaml because it puts in new lines breaking go template

@rohith-vallabhaneni
Copy link

rohith-vallabhaneni commented May 1, 2019

By any chance can I know if this feature is implemented already, I'm getting |+ at the start of the yaml output which is effecting my parsing. I could ignore it my perforning some data maipulations but i'm writing many yaml files to stdout out of which only one file is throwing this issue :(

Could anyone suggest me any other work around, thanks!

Please find the block of output that is effecting me

metrics:

  • type: Resource
    object: null
    pods: null
    resource:
    name: cpu
    targetaverageutilization: 70
    targetaveragevalue: null
    --- |+
    apiVersion: v1

@alexandrevicenzi
Copy link

@rohith-vallabhaneni there's an open PR (#455), you can apply this patch locally and wait until is merged and released.

@laverya
Copy link
Contributor

laverya commented May 2, 2019

@rohith-vallabhaneni that patch isn't quite as simple as 'switch to the patch and everything works', I'm afraid - you'll need to edit your calling code to set the desired line length too, as in this comment: #166 (comment)

@bt
Copy link

bt commented Oct 30, 2019

Any updates on this issue? It's breaking my application config files as I have some strings that are long and it's being spanned across 2 lines.

@kivutar
Copy link

kivutar commented Nov 14, 2019

I think that line length should default to -1. This value of 80 is arbitrary. This behavior is surprising.

@laverya
Copy link
Contributor

laverya commented Nov 14, 2019

Honestly, that's probably just as good of a solution as adding a function to set line length for my purposes. Better backwards compatibility too

@ChanderG
Copy link

ChanderG commented Nov 19, 2019

Facing this issue as well. Using the workaround recommended by tyangliu for now.

Edit: Turns out that adding newlines to fields breaks other things (k8s in our case). Using a locally patched version of yaml with yaml_emitter_set_width -1 in the newEncoder function itself.

@niemeyer niemeyer changed the title Marshalling long, single-line strings results in invalid line breaks Marshalling long, single-line strings results in (correct) line breaks Nov 19, 2019
@niemeyer
Copy link
Contributor

@laverya That's what we'll do indeed. 👍

@laverya
Copy link
Contributor

laverya commented Nov 25, 2019

@niemeyer would you mind if I opened a PR for that? (setting default line length to -1, I mean)

@matteomazza91
Copy link

I think this could be closed now.
for whoever is still looking into this today, v2 has now the ability to disable line wrapping. See additional details in the doc: https://pkg.go.dev/gopkg.in/yaml.v2#FutureLineWrap

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests