3

I have a PowerShell script to take a JSON file as a parameter and convert it to object. In the JSON string (as a template) there may have some variables which should be replaced with the actual values during runtime. For example, $var_1, $var_x and $var_y in this JSON string:

{
    "key1": "My name is ${var_1}",
    "key2":
    {
        "key3": "Another variable ${var_x}",
        "key4": "${var_y}"
    }
}

The names of these variables are unknown to the script. I use ExpandString to do the job:

[string]$jsonText = Get-Content $inputFile;

if ([string]::IsNullOrWhiteSpace($jsonText)) {
    throw "No content in $inputFile"
}
$temp = $ExecutionContext.InvokeCommand.ExpandString($jsonText);
$jsonObj = ConvertFrom-Json $temp;

The problem is, when a variable contains double quotation mark, then calling ConvertFrom-Json will fail because the quotation mark is not escaped. For example, "My name is ${var_1}" may become "My name is a "new" name".

Is there a way to escape these string variables when calling ExpandString?

2 Answers 2

2

You can use the following technique, taking advantage of being able to embed entire statements in your template via $(...) the subexpression operator; therefore, it should also be noted that you should generally only use this technique with template strings that you trust, given that arbitrary commands could be executed.

$var_1 = '"new" name'

$template = @'
{ "key1": "My name is $((Get-Variable var_1).Value -replace '"', '\"')" }
'@

$ExecutionContext.InvokeCommand.ExpandString($template)

The above yields:

{ "key1": "My name is \"new\" name" }

which is valid JSON.

Note the use of Get-Variable to access the value of variable ${var_1} indirectly, so that its embedded " chars. can be safely escaped as \", as required by JSON.


Since the above is a little cumbersome for multiple references, you could define an auxiliary function:

$var_1 = '"new" name'

# Aux. function that takes a variable name and returns its value with
# " chars. \-escaped.
function esc ($varName) { (Get-Variable $varName).Value -replace '"', '\"' }    

# Now you can reference the variables using $(esc <var-name):
$template = @'
{ "key1": "My name is $(esc var_1)" }
'@

$ExecutionContext.InvokeCommand.ExpandString($template)

If you don't control the template string, more work is needed:

$var_1 = '"new" name'

# Aux. function that takes a variable name and returns its value with
# " chars. \-escaped.
function esc ($varName) { (Get-Variable $varName).Value -replace '"', '\"' }

# The original template with ${var}-style references.
$template = @'
{
  "key1": "My name is ${var_1}",
}
'@

# Modify the template to replace ${var} references with $(esc var).
$modifiedTemplate = $template -replace '\$\{(\w+)\}', '$$(esc $1)'

# Now expand the modified template.
$ExecutionContext.InvokeCommand.ExpandString($modifiedTemplate)

Note that the template transformation assumes: Tip of the hat to briantist for his input.

  • that there are no escaped variable references to be treated as literals in the input template (e.g., `${var}).

  • that all variable references have the form ${varName}, where varName is assumed to be composed of letters, digits, and underscores only (matching one or more \w instances).

A more robust solution that covers all edge cases may require using PowerShell's parsing API, which requires significantly more effort, however.

Sign up to request clarification or add additional context in comments.

Comments

0

Is it allowed in that format? If the variable is quoted with "", it's a string, and won't add the marks to .json. e.g.:

$var_1 = "Json Sample"
$json_piece = @"
{
    "key1": "My name is $var_1",
    "key2":
    {
        "key3": "Another variable $var_2",
        "key4": "$var_3"
    }
}
"@

It outputs ($var_2 and $var_3 are not defined):

{
    "key1": "My name is Json Sample",
    "key2":
    {
        "key3": "Another variable ",
        "key4": ""
    }
}

If you add " to a json file like this:

{
    "key1": "My name is "Json Sample"",
    "key2":
    {
        "key3": "Another variable ",
        "key4": ""
    }
}

You will make an error: Excepted comma. It means if the variable contains "", you must escape them at first, if not, the .json file is unexpected.


P.S. Validation I validated it on FREEFORMATTER.COM.
It's my code:

{
  "Key": "value is "value"",
  "key2": "other value"
}

And I get 2 errors:

  • The JSON input is NOT valid according to RFC 4627 (JSON specification). Unexpected token v at position 20.
  • The JSON input is NOT valid in JavaScript, missing } after property list (At line #2), (At position #24)

5 Comments

The point is that here it's like $var_1='"JsonSample"', so once the replacement happens, the resulting JSON does become invalid. They don't know what value the variable will actually contain at run-time.
If you use " in your .json file, you will get errors. " must be escaped before passing to the .json file. I just tried that.
I added the validation to my answer.
Perhaps I'm misunderstanding, but you seem to be restating the problem posed in the question.
Hmm, I found that, the point of this question is how to escape double quotation mark from the variable. @mklement0 made a good answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.