How do I preserve spaces in retrieving json keys? - json

I am using vba-json to parse json and am having trouble preserving spaces in keys. I am new to VBA and didn't see anything in the class to give me the option to preserve spaces in keys.
I am using the class found here
I have:
Function me_()
Dim s, json, i
s = "{'key one':'value one','key two':'value two'}"
Dim lib As New JSONLib
Set json = lib.parse(CStr(s))
For Each i In json
Debug.Print i & "," & json.Item(i)
Next
me_ = "done"
End Function
This preserves the spaces in the values but not the keys:
keyone,value one
keytwo,value two
(jsonlint.com says my json is valid with the spaces in the keys)

It's possible by changing the code, specifically a method parseKey.
Whitespaces (spaces, tabs and various linebreaks) are ignored in keys on line 282:
If InStr(vbCrLf & vbCr & vbLf & vbTab & " ", char) Then

Related

Build large string in VBA with quotes

I need to build a large string (json) in VBA with quotes in it.
I tried this approach:
Dim payload As String
payload = ""
payload = payload & CHR(34) & {"timeframeId":13,"startDate":"2021-05-31 00:00:00.000","endDate":"2021-05-31 00:00:00.000","peopleList":[{"id":123}], & CHR(34)
...more lines building string
But the Excel IDE for VBA complains of a syntax error - presumeably to do with the quoted strings?
Do I need to add CHR(34) before the opening and after closing " of each?
Al Grant!
Chr(39) or " double quote char is the text qualifier to VBA, so you need to do an ajustment to use in the strings.
I presume you have this value anywhere in the worksheet.
So the common way to do this is set the range and use replace to another char, and after all back to chr(39).
If you what to input in hardcode, you will need to concatenate chr(39) with everytime a doublequote appears, or use ""
But the easiest way is to replace find and replace (CTRL+H) to another char, and continue to develop like you was doing
Dim payload As String
payload = payload & Chr(34) & Replace("{'timeframeId':13,'startDate':'2021-05-31 00:00:00.000','endDate':'2021-05-31 00:00:00.000','peopleList':[{'id':123}],", Chr(39), Chr(34)) & Chr(34)
Debug.Print payload
Result:
"{"timeframeId":13,"startDate":"2021-05-31 00:00:00.000","endDate":"2021-05-31 00:00:00.000","peopleList":[{"id":123}],"

I am having problems with double-quotes when updating a value in a json file

I am reading a json file that's a configuration file for a 3D slicer engine. I am using the following code (summarized, not complete)
Dim json As String = File.ReadAllText("C:\tmp\theme.json")
Dim jsonObj As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(json)
I am reading a specific value from the file and putting the value (RGB values) into a text box and showing the found color in a picturebox:
Dim read = Newtonsoft.Json.Linq.JObject.Parse(json)
col = read.Item("colors")("layerview_inset_0").ToString
col = Replace(col, "[", "")
col = Replace(col, "]", "")
col = Trim(col)
TextBox1.Text = Trim(Replace(col, " ", ""))
cols = Split(col, ",")
PictureBox1.BackColor = Color.FromArgb(cols(0), cols(1), cols(2))
This all works fine.
What I do next is load a colorpicker by clicking on the picturebox and selecting a different color :
With ColorDialog1
'Get the current color from the picturebox
.Color = ColorDialog1.Color
If .ShowDialog = DialogResult.OK Then
Me.PictureBox1.BackColor = .Color
TextBox1.Text = .Color.R.ToString & "," & .Color.G.ToString & "," & .Color.B.ToString & "," & "255"
code = "[" & .Color.R.ToString & "," & .Color.G.ToString & "," & .Color.B.ToString & "," & "255" & "]"
End If
End With
This creates a string value (code) that I then use to replace the key in the file:
jsonObj("colors")("layerview_inset_0") = code
and then write back the updated file
Dim output As String = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented)
File.WriteAllText("C:\tmp\theme.json", output)
This also works fine but the value inserted is wrapped in double quotes.
The Original Value in the file is like this :
"layerview_inset_0": [
255,
255,
255,
255
],
But after my update it is :
"layerview_inset_0": "[0,0,160,255]",
This causes the program to fail when loading the file.
If I remove the double-quotes it works OK.
I haven't worked with json files before and it has taken ages to get this far. How can I add the data I need into the json file and NOT have it wrapped in double-quotes?
I expect it's because I am inserting the value as a string but I don't know any other way to do it as I need to make sure I also add the [ and ] as well as each of the RGB values like the original format.
Everything works well except the addition of the double-quotes around the new value in the file, but how do I add this data to the file without it happening?
I would appreciate any guidance on this, bearing in mind that this is my first attempt at parsing a json file and replacing a key value, so please be kind. I'm probably doing it all wrong!

"Expected End of Statement" when concatenating string

I am attempting to embed an PDF iframe viewer into a web based form I am building.
I have done this multiple times but for the life of me I can not get it right this time.
''This attaches a PDF uploaded on a previous form and should display it within
''an iFrame.
If "aObjects("RD20_AttachRandR")" <> "avar1" Then
fcLabel = "<iframe src=""" & "aObjects("RD20_AttachRandR ")" & ".PDF" & _
" width=800px height=1000px ><p>Your browser does not support iframes.</p></iframe>"
End If
Somewhere in the line starting with fcLabel I am missing an " that ends the string that I am passing through. But I am unable to find it.
Presumably aObjects is a dictionary (or other collection) variable, so you need to remove the outer double quotes. Also, the second time you use that variable the item name string has a trailing space ("RD20_AttachRandR ") which you may want to remove.
If aObjects("RD20_AttachRandR") <> "avar1" Then
fcLabel = "<iframe src=""" & aObjects("RD20_AttachRandR") & ".PDF" & _
" width=800px height=1000px ><p>Your browser does not support iframes.</p></iframe>"
End If
As noted by Ansgar Wiechers,
I had forgotten to remove a trailing space at the end of the aObject name.
I removed it and it works now.
Ta.

Line feeds being replaced with string

I'm trying to append a character string from a variable to the end of lines in a CSV. However, line feeds are being replaced by the string. So if the original line is:
fieldcharacters1,fieldcharacters string starts on one line
and continue after a line feed,fieldcharacters3
(where the blank line is a line feed)
and I want to append "fieldcharacter4" (without quotes) to the end of the line, it should show up like this:
fieldcharacters1,fieldcharacters string starts on one line
and continue after a line feed,fieldcharacters3,fieldcharacter4
But instead it's ending up like this:
fieldcharacters1,fieldcharacters string starts on one
line,fieldcharacter4 ,fieldcharacter4 and continue after a line
feed,fieldcharacters3,fieldcharacter4
Here's the code I have. I also tried a Do Until Loop using objCurrentFile.AtEndOfStream, but it didn't work either.
Set tsOut = objFSO.CreateTextFile(strCSV, FOR_WRITING)
strLine = objCurrentFile.ReadAll
objCurrentFile.close
strInsertText = ",testing123123" & chr(013)
WScript.Echo "Text to be appended= " & strInsertText
strNewText = Replace(strLine, Chr(013), strInsertText )
tsOut.Write strNewText
There are three end-of-line markers:
Linefeed|vbLf|Chr(10)|/n
CarriageReturn|vbCr|chr(13)|/r
CarriageReturn+Linefeed|vbCrLf|Chr(13) & Chr(10)|/r/n
When reading from a file, .ReadLine() will remove /n or /r/n, but not /r (see here); .ReadLine(), however, will return all contents of the file.
A simple model of your problem: you have a .CSV containing fields with embedded \r's and \r\n as eol; when 'trying to append a character string from a variable to the end of lines' via Replace, the embedded \r's are affected too:
>> sLine = "field 1,... one line\rand ...field 3\r\n"
>> WScript.Echo Replace(sLine, "\r", "[,append]")
>>
field 1,... one line[,append]and ...field 3[,append]\n
You may get away with Replacing \r\n:
>> WScript.Echo Replace(sLine, "\r\n", "[,append]")
>>
field 1,... one line\rand ...field 3[,append]
and dealing with an eventual replacement of a trailing \r\n in your file. If you can loop over the .CSV lines, append/concatenate (instead of using .Replace) the extra data and the proper eol marker (perhaps via .WriteLine).
The first approach will fail if the eol- and the embedded marker are identical. In that case (and probably in general) using the ADO Text driver to deal with .CSVs is a much better option.
(BTW: read the docs to learn why
objFSO.CreateTextFile(strCSV, FOR_WRITING)
is wrong)
Update wrt comment:
From your comment I conclude that your .CSV use \r\n for eol and embedded (paragraph?) marker. So Replace won't work:
>> sLine = "field 1,... one line\r\nand ...field 3\r\n"
>> WScript.Echo Replace(sLine, "\r\n", "[,append]")
>>
field 1,... one line[,append]and ...field 3[,append]
When using .ReadLine() you also have to expect to get 'short lines' terminated at the embedded \r\n. So your options are:
Talk to the creator of the .CSV and ask her to use different markers for the embedded vs. the eol marker. You can then use Replace(.ReadAll(), eol, extra & eol, provided you deal with the 'spurious replacement on the last (empty) line' mentioned above.
Depending on the format/layout/content of the .CSV - quoted strings, field separator embedded in (quoted) fields, data types, line length(es), position of \r\n in a line - you may be able to write a custom RegExp replacement function or a full-fledged parser that does the job (cf here). To help you with that I'd need a decent desription of the .CSV (think schema.ini) and representative sample data.
Use ADO text driver (start your research here, but don't stop there). The risk: I'm not sure whether the driver can cope with (mis)using the eol marker embedded.
A rough idea:
After a short look at the sample data, I can confirm that \r\n is used for eol and paragraph in some of the quoted fields. First tests show that the ADO text driver can deal with that.
But because the first field contains quoted IP Adresses, you can distinguish between eol (\r\n followed by " (or "IP Address")) and paragraph (\r\n not followed by " (or "IP Address")). If there are no " in the 'paragraphed' fields, a (not so) simple Replace for \r\n" on the .ReadAll will solve the problem; otherwise a RegExp refering to the IP Address would be needed.
Demo code:
Option Explicit
Dim sCsv : sCsv = Replace(Join(Array( _
"'1.2.3.4','line1\r\nline2',1,'whatever'" _
, "'5.6.7.8','linea\r\nlineb',2,'pipapopu'" _
, "" _
), "eol"), "'", """")
WScript.Echo "faked .CSV (embedded \r\r, EOL \r\n)"
WScript.Echo Replace(sCsv, "eol", "\r\n" & vbCrLf) ' for display
sCsv = Replace(Replace(sCsv, "eol", vbCrLf), "\r\n", vbCrLf) ' 'real data'
WScript.Echo "raw display of .CSV"
WScript.Echo sCsv
If False Then
Dim re : Set re = New RegExp
re.Global = True
re.Pattern = "(\r\n""\d+(\.\d+){3}"")" ' EOL followed by "IP Address" of next line
' dirty hack to append to last line
sCsv = sCsv & """0.0.0.0"""
sCsv = re.Replace(sCsv, ",""append""$1")
' remove trailing junk (\r\n"IP Address")
sCsv = Left(sCsv, Len(sCsv) - 11)
Else
' dirty hack to append to last line
sCsv = sCsv & """"
sCsv = Replace(sCsv, vbCrLf & """", ",""append""" & vbCrLf & """")
' remove trailing junk (\r\n")
sCsv = Left(sCsv, Len(sCsv) - 3)
End If
WScript.Echo "raw display of .CSV after appending"
WScript.Echo sCsv
output (for both methods):
cscript 24673618.vbs
faked .CSV (embedded \r\r, EOL \r\n)
"1.2.3.4","line1\r\nline2",1,"whatever"\r\n
"5.6.7.8","linea\r\nlineb",2,"pipapopu"\r\n
raw display of .CSV
"1.2.3.4","line1
line2",1,"whatever"
"5.6.7.8","linea
lineb",2,"pipapopu"
raw display of .CSV after appending
"1.2.3.4","line1
line2",1,"whatever","append"
"5.6.7.8","linea
lineb",2,"pipapopu","append"

remove unwanted chr(13) from csv string with classic asp (vbscript)

I want to create a classic asp (vbscript) function that replaces all 'returns' that occur between double quotes.
The input string is 'csv' like:
ID;Text;Number
1;some text;20
2;"some text with unwanted return
";30
3;some text again;40
I want to split the string on chr(13) (returns) to create single rows in an array. It works well, except for the unwanted chr(13) that is contained in the text of id 2.
I hope someone could help.
Fundamentally, this is going to be difficult to do as you won't be able to tell whether the carriage return is a valid one or not. Clearly the ones after 20 and 30 are valid.
An approach I would would be to scan through each line in the file and count the commas that occur. If it's less than 3, then append the next line and use the concatenated string. (This of course assumes your CSV structure is consistent and fixed).
What I would really be asking here is why is the CSV like this in the first place? The routine that populates this should really be the one stripping the the CRs out.
Think of a CSV file like a very crude database or spreadsheet. When cosidering the above file, it is clear that the 'Database'/'Spreadsheet' is corrupt.
If the program that generates this is correupting it, then what extent should the reading application goto to correct these defects? I'm not sure that Excel or SQL Server (for example) would go to great lengths to correct a corrupt data source.
Your text file is just like a CSV file but with semicolons not commas. Use ADO to grab the data and it will handle the line breaks in fields.
Specifically (In ASP VBScript):
On Error Resume Next
Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adCmdText = &H0001
Set objConnection = Server.CreateObject("ADODB.Connection")
Set objRecordSet = Server.CreateObject("ADODB.Recordset")
strPathtoTextFile = server.mappath(".") 'Path to your text file
objConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & strPathtoTextFile & ";" & _
"Extended Properties=""text;HDR=YES;FMT=Delimited"""
objRecordset.Open "SELECT * FROM test.txt", _
objConnection, adOpenStatic, adLockOptimistic, adCmdText
Do Until objRecordset.EOF
Response.Write "ID: " & objRecordset.Fields.Item("ID") & "<br>"
Response.Write "Text: " & objRecordset.Fields.Item("Text") & "<br>"
Response.Write "Number: " & objRecordset.Fields.Item("Number") & "<br>"
objRecordset.MoveNext
Loop
Code sample is modified from Microsofts' Much ADO About Text Files.
This script assumes your data text file is in the same directory as it (the asp file). It also needs a schema.ini file in the same directory as your data text file with the data:
[test.txt]
Format=Delimited(;)
Change text.txt in both code samples above to the name of your text file.
If the unwanted CRLF always occurs inside a text field (inside double quotes), it would not be very difficult to use a regular expression to remove these. Vbscript has a regex engine to its disposal: http://authors.aspalliance.com/brettb/VBScriptRegularExpressions.asp
It all depends ofcourse on how familiar you are with Regular Expressions. I couldn't think of the proper syntax off the top of my head, but this is probably quite easy to figure out.
The solution is pretty easy:
str = "Some text..." & chr(13)
str = REPLACE(str,VbCrlf,"")
The secret is use VbCrlf. For me I use a simple function for solve the problem and add this in my framework.
FUNCTION performStringTreatmentRemoveNewLineChar(byval str)
IF isNull(str) THEN
str = ""
END IF
str = REPLACE(str,VbCrlf,"")
performStringTreatmentRemoveNewLineChar = TRIM(str)
END FUNCTION
Of course this will remove all new lines character from this string. Use carrefully.