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

Impossible to get string with single backslash in output #679

Closed
alikhil opened this issue Jul 4, 2019 · 8 comments
Closed

Impossible to get string with single backslash in output #679

alikhil opened this issue Jul 4, 2019 · 8 comments

Comments

@alikhil
Copy link

alikhil commented Jul 4, 2019

I am using jsonnet to write configurations for drone ci. Drone uses jsonnet to generate yaml file and use it as instruction for CI/CD.

For my needs I want to have the following output:

{ "some-field" : "${DRONE_BRANCH/\//-}" }

I tried the following jsonnet script:

{
  "some-field": "${DRONE_BRANCH/\//-}",
}

And it gave me:

{
   "some-field": "${DRONE_BRANCH///-}"
}

Then I tried:

{
   "some-field": "${DRONE_BRANCH/\\//-}"
}

It gave me:

{
   "some-field": "${DRONE_BRANCH/\\//-}"
}

I did not find a way to achieve what I want. Seems to me it's currently impossible.

P.S. I am using the latest version of jsonnet

$ jsonnet -v
Jsonnet commandline interpreter v0.13.0
@Raiondesu
Copy link

+10 for this. How come single slashes are not allowed in a JSON?
This is ridiculous.

@sbarzowski
Copy link
Collaborator

The thing is that in JSON "/" and "\/" are equivalent, i.e. they describe the same sequence of characters (i.e. a single character U+002F). If I'm interpreting the JSON specification correctly, every compliant implementation should treat them the same per RFC 7159 8.3 String Comparison.

Similarly JSON strings "${DRONE_BRANCH/\//-}" and "${DRONE_BRANCH///-}" refer to exactly the same sequence of characters ${DRONE_BRANCH///-}. If you want a JSON string which denotes a string with a backslash there (e.g. because you feed it to a script which requires an escaped slash there), you should actually escape the backslash. So if you want a JSON meaning ${DRONE_BRANCH/\//-}, the right JSON is "${DRONE_BRANCH/\\//-}.

Python agrees btw:

>>> json.dumps(json.loads('"${DRONE_BRANCH/\\//-}"')) # double escaping
'"${DRONE_BRANCH///-}"'
>>> x = '"${DRONE_BRANCH/\\//-}"' # double escaping
>>> print(x)
"${DRONE_BRANCH/\//-}"
>>> print(json.loads(x))
${DRONE_BRANCH///-}
>>> x = '"${DRONE_BRANCH/\\\\//-}"' # double escaping
>>> print(x)
"${DRONE_BRANCH/\\//-}"
>>> print(json.loads(x))
${DRONE_BRANCH/\//-}

Escaping / is unnecessary, so Jsonnet doesn't put it there. Jsonnet generally goes for minimally escaped strings in the output.

Please let me know if that answers your question.

@Raiondesu
Copy link

Raiondesu commented Jul 4, 2019

@sbarzowski, though it's a comprehensive answer, I suggest you pay more attention when reading the question.

Then I tried:

{
  "some-field": "${DRONE_BRANCH/\\//-}"
}

It gave me:

{
  "some-field": "${DRONE_BRANCH/\\//-}"
}

In other words, escaping the escaping backslash just produces two backslashes.

Therefore, the last bit of "example output" you've provided is not how jsonnet handles this.

Instead, it actually produces

>>> print(x)
"${DRONE_BRANCH/\\//-}"
>>> print(json.loads(x))
${DRONE_BRANCH/\\//-}

@sbarzowski
Copy link
Collaborator

sbarzowski commented Jul 4, 2019

Instead, it actually produces

Did you actually run it in Python? This was directly copy&pasted from Python shell. Here you can see it yourself: https://www.onlinegdb.com/S1g978ilH.

In other words, escaping the escaping backslash just produces two backslashes.

Yes, because it also needs to be escaped in the output JSON. I'm arguing that it is the correct behavior. And that trying to get JSON output { "some-field" : "${DRONE_BRANCH/\//-}" } specifically and not { "some-field" : "${DRONE_BRANCH///-}" } is probably a wrong goal, because these two are equivalent. These two JSONs mean exactly the same thing, so why would you prefer the one with unnecessary escaping?

For completeness I'll add that you can use the "raw string" mode (-S option) to do your own serialization if you really want to. Then your program must produce a string, which will be printed without any additional escaping. But as long as your output is standard JSON you shouldn't have to.

@sbarzowski
Copy link
Collaborator

In other words if you want it to be loaded with a single backslash you need these two backslashes in JSON.

E.g.:

➜ jsonnet -e '{
   "some-field": "${DRONE_BRANCH/\\//-}"
}' | python -c 'import json; import sys; print(json.load(sys.stdin)["some-field"])'
${DRONE_BRANCH/\//-}

With a single backslash it won't work as expected (it will be interpreted as escaped / which is just /):

➜ cat foo.json
{ "some-field" : "${DRONE_BRANCH/\//-}" }
➜ cat foo.json | python -c 'import json; import sys; print(json.load(sys.stdin)["some-field"])'
${DRONE_BRANCH///-}

@sbarzowski
Copy link
Collaborator

I'm going to assume that it's all clear now after my last explanation. If it's not, feel free to reopen.

@sparkprime
Copy link
Member

Just came here to add that in YAML, you can specify a single character string containing a backslash as either:

\

or

"\\"

In JSON, only the latter is supported. When Jsonnet generates YAML with std.manifestYAMLDoc, it chooses to emit YAML in the second form, because implementing that is simpler, and it's equivalent. Drone CI should interpret "\\" in the way that you want.

@sparkprime
Copy link
Member

Oh and just for completeness -- the various ways of specifying it in Jsonnet:

[
  "\\",
  '\\',
  @"\",
  @'\',
  // Note, this last one also adds a \n, see #289
  |||
    \
  |||,
]

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

4 participants