Vicky Dev
Vicky Dev

Reputation: 2183

Powershell force convert the ConvertFrom-Json output to string which can then be used in all string operations incl. filenaming

As I have mentioned in the Question title, I want to convert the ConvertFrom-Json command's output to string such that, the character/string I get, should be usable such that it can be inserted into DateTime string as replacement to another character.

Currently I have following code to get the present DateTime:

$DTCurr = (Get-Date).tostring("dd-MM-yyyy_hh+mm+ss")

Now in the above code, I want to force replace the plus sign with colon sign, such that the resulting DateTime string can be used in the file-naming, so I am expecting the output(after replacement) like below:

07-11-2020_12:59:13

Now I tried this code for that forced replacement, but it doesn't work:

$colon = ('{ "str": "\uA789" }' | ConvertFrom-Json)
$DTCurr = (Get-Date).tostring("dd-MM-yyyy_hh+mm+ss")
$DTCurr = $DTCurr -replace "\+",$colon
Echo $DTCurr

This gives the output: 07-11-2020_02@{str=꞉}06@{str=꞉}28 which is ridiculous and unexpected. I can assure that $colon does print : when passed to Echo.

Can someone let me know what I doing wrong and help out achieve this ?

Upvotes: 1

Views: 398

Answers (2)

mklement0
mklement0

Reputation: 439487

PowerShellGuy's helpful answer solves your problem; let me complement it:

tl;dr

# Use a [char] cast with the Unicode code point to create the char.
# of interest - no need for using JSON for that.
PS> (Get-Date).ToString('dd-MM-yyyy_hh+mm+ss') -replace '\+', [char] 0xA789
06-11-2020_09꞉17꞉25

It seems that the sole reason you're using a JSON representation is to get a string with a Unicode character beyond the ANSI/ASCII-range, namely (MODIFIER LETTER COLON, U+A789), which looks just like the ASCII-range : (COLON, U+003A), but isn't.

If we assume that you need this JSON detour - which you don't - the simplest solution would have been:

$colonSubstitute = '"\uA789"' | ConvertFrom-Json

The JSON detour isn't needed, because you can cast Unicode code points directly to [char] (System.Char):

# Directly creates Unicode char. U+A789 as a [char] (System.Char) instance.
$colonSubstitute = [char] 0xA789

You could cast that to a [string] instance, though that is often not necessary, given PowerShell's automatic, flexible type conversions (see below):

$colonSubstitute = [string] [char] 0xA789

PowerShell [Core] v6+ directly supports Unicode escape sequences (akin to JSON's) inside double-quoted strings ("..."), also known as expandable (interpolating) strings, using the syntax `u{n}, where n is the character's Unicode code point:

# PowerShell [Core] v6+ escape sequence
# Same as:         "$([char] 0xA789)"
$colonSubstitute = "`u{A789}"

Note: Unlike [char] casts, the `u{n} syntax also supports characters beyond the Unicode BMP (Basic Multilingual Plane), i.e., characters with code points greater than U+FFFF (0xFFFF); e.g., "`u{1F913}" for 🤓. However, in the resulting (expanded) string such characters are represented as two [char] (System.Char) instances, so-called surrogate pairs, because .NET characters are UTF-16, i.e. 16-bit code units with a max. value of 0xFFFF and therefore cannot directly represent non-BMP characters; thus, for instance, "`u{1F913}".Length yields 2.


In Windows PowerShell, you can use $(...), the subexpression operator, to embed [char] casts inside double-quoted strings ("..."):

$colonSubstitute = "$([char] 0xA789)"

Note: As discussed, [char] (System.Char) casts are limited to characters in the Unicode BMP. While characters in the non-BMP range (code points 0x10000 and up) are rare overall, you do need them for emoji, such as 🤓 (NERD FACE , U+1F913). Unlike the PowerShell [Core] v6+ syntax, using [char] casts to represent surrogate pairs is neither obvious nor convenient:

For instance, to represent 🤓, you must (a) know that non-BMP code point U+1F913 is represented as UTF-16 surrogate pair 0xD83E, 0xDD13, and then embed the latter in either of these two forms:
"$(-join [char[]] (0xD83E, 0xDD13))" or "$([char] 0xD83E)$([char] 0xDD13)"


Finally, given PowerShell's automatic, flexible type conversions, you can directly use a [char] instance as the operands of the -replace operator:

PS> (Get-Date).ToString('dd-MM-yyyy_hh+mm+ss') -replace '\+', [char] 0xA789
06-11-2020_09꞉17꞉25

Upvotes: 1

PowerShellGuy
PowerShellGuy

Reputation: 801

Sorry if I'm misconstruing this, but I think your end goal can be simplified by doing this

$DTCurr = (Get-Date).tostring("dd-MM-yyyy_hh:mm:ss")

or this

$DTCurr = (Get-Date).tostring("dd-MM-yyyy_hh+mm+ss")
$colonDT = $DTCurr -replace "\+",":"

but if you wanna do it your way, the reason why it's printing that output, is because it's doing exactly what you're telling it to do. You're replacing the + with an object that has a property named str with a value of :. You would need to do this instead

$colon = ('{ "str": "\uA789" }' | ConvertFrom-Json)
$DTCurr = (Get-Date).tostring("dd-MM-yyyy_hh+mm+ss")
$colonDT = $DTCurr -replace "\+",$colon.str
Echo $colonDT

If I am incorrect, and you need more assistance, let me know.

Upvotes: 2

Related Questions