1

I've read that json.loads() can be used by Robot Framework to convert a JSON string to a dictionary in this post: Json handling in ROBOT

So if you define a dictionary-like string like this:

${json_string}    Set Variable    {"key1": "value1", "key2": "value2", "key3": "value3"}

You can then use the following to convert it to a dictionary:

${dict}    Evaluate    json.loads('''${json_string}''')    json

My question is simple - why are the triple quotes needed here to surround the argument?

If single quotes are used an exception is thrown stating a string must be used:

${dict}    Evaluate    json.loads('${json_string}')    json

(Edit) The above is a bad example, it actually works. If double quotes are used, though, it fails for SyntaxError: invalid syntax.

If no quotes at all are used an error occurs that indicates that the variable is a dictionary - but in Robot it isn't a dictionary (TypeError: the JSON object must be str, bytes or bytearray, not dict):

${dict}    Evaluate    json.loads(${json_string})    json

If Robot's Convert To String is used on the ${json_string} variable and then that new variable is passed to the json.loads() method the same TypeError occurs stating a string must be used, not a dictionary - but it has been converted to a string:

${json_string2}    Convert To String    ${json_string}
${dict}    Evaluate    json.loads(${json_string2})    json

What are the triple quotes accomplishing that are not being accomplished by the other two? This seems to be an aspect of Robot framework...

yaroo
  • 31
  • 1
  • 5
  • 3
    JSON can contain `"` and `'` characters. Python's triple-quoted strings allow single quote characters inside them unescaped. – Iguananaut Apr 14 '20 at 00:27
  • Thanks for the answer Iguananaut, although, my example does not have any single quote characters. Your answer is probably a good reason as to why one should in general use triple quotes in this scenario - to preserve things like newlines in the ${json_string} variable, or other characters that perhaps could get converted. But it does not explain why using no-quotes fails or attempting to convert the variable to a string then passing that string also fails - because they're recognized as dictionaries - but to Robot they are not dictionaries... – yaroo Apr 21 '20 at 03:40

1 Answers1

1

I'll go ahead and follow up in an answer since it's simpler that way, I think does answer your follow-up question in this comment.

I'm not very familiar with Robot Framework but if I understand correctly all it's doing in .robot files when you use ${variable} substitution is simple string templating: so when you pass a ${variable} into an expression, no matter what type the underlying variable is, it always substitutes its string representation, so the expression you're trying to evaluate is literally:

json.loads({'key': "value1', 'key2': 'value2'})

This is why you need the """ (in principle you could use just ", but """ would be much safer). Incidentally, if you converted the above dict to its standard string representation in Python it would not be valid JSON, because JSON requires double-quotes not single-quotes.

The official documentation on this is a little confusing and seems to contradict itself (I think it was not written by a native English speaker which is OK):

When a variable is used in the expressing using the normal ${variable} syntax, its value is replaces before the expression is evaluated. This means that the value used in the expression will be the string representation of the variable value, not the variable value itself. This is not a problem with numbers and other objects that have a string representation that can be evaluated directly, but with other objects the behavior depends on the string representation.

I say it contradicts itself because first is says "its value [is replaced] before the expression is evaluated". But then it says "the value used in the expression will be the string representation of the variable value" (which is not the same as the value itself). The latter explanation seems to be the correct one though.

However, it seems with Evaluate you can also use the syntax $variable to replace the literal value of the variable instead of its string representation:

Starting from Robot Framework 2.9, variables themselves are automatically available in the evaluation namespace. They can be accessed using special variable syntax without the curly braces like $variable. These variables should never be quoted, and in fact they are not even replaced inside strings.

But in your case you wrote:

${json_string}    Set Variable    {"key1": "value1", "key2": "value2", "key3": "value3"}

As I understand it "Set Variable" just stores the value as a string literal. So indeed it should suffice then to run (as you did):

${dict}    Evaluate    json.loads('''${json_string}''')    json

In your final example "Convert To String" is not doing anything, because as you wrote ${json_string} already replaces as a string. You just have to understand that the result of json.loads(${json_string}) is a Python expression where ${json_string} is just replaced literally with the contents of that template variable--it is not the same as passing a str value to json.loads(). The latter I believe may be achievable as

${dict}    Evaluate    json.loads($json_string)    json

at least, going by the docs. But I have not tested this.

Iguananaut
  • 21,810
  • 5
  • 50
  • 63
  • Thanks for pointing out the literal value syntax. `Evaluate json.loads($json_string) json` works and `Evaluate json.loads($json_string2) json` also works. But, Robot does not (when inspected via Eclipse or with Log To Console) transform either ${json_string} or ${json_string2} into a single quoted representation like so: {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'} - if it did a different error occurs (JSONDecodeError). So that part will have to remain a mystery as to why json.loads() then pukes when the content looks acceptable without triple quotes. – yaroo Apr 23 '20 at 04:58
  • I guess, maybe, it does create a dict literal but then convert it to a string. I don't know why it does that. – Iguananaut Apr 23 '20 at 09:58
  • 1
    @Iguananaut: your original statement was correct. In this case, `Set Variable` creates a string that looks like a dictionary, but it's still just a string within robot. However, once robot performs variable substitution, the end result becomes a literal dictionary. In other words, yes, `json.loads($json_string)` is the correct way to do it since `${json_string}` is in fact a string. – Bryan Oakley Jul 30 '21 at 05:58
  • @BryanOakley Thank you for your expert confirmation ^^ – Iguananaut Jul 30 '21 at 07:43