I am generating some HTML in VBA (MSACCESS), which works fine but it is a bit of a mess from an indentation point of view.
Is there an easy way to indent a stream of HTML text in VBA?
I use Visual Studio Code format functionality to have a prettier HTML, but I have to do this by hand and it is very tedious!
Example:
<div class="anythinggoes">
<ul><li>A</li>
<li>B</li><li>C</li>
</ul></div> <!-- anythinggoes -->
Should become something like:
<div class="anythinggoes">
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div> <!-- anythinggoes -->
Any help will be much appreciated!
CAUTION! Ugly code ahead!
Welcome on board, RichD. I think this code might help you:
First, define these variables in the Module scope:
Private InlineTags As Variant
Private InlineClosingTags As Variant
Private LineBreakTags As Variant
Then, we can use this function:
Function ReadableHTML(HTML As String) As String
Dim a$, i&, TabsNo&, tabs$, l&, tag$, MaxTabs&
'add here tags that you want to keep on the same line of their parent
InlineTags = Array("!--", "a", "i", "b", "sup", "sub", "strong") 'never followed by a line break
InlineClosingTags = Array("li", "h1", "h2", "h3", "h4") 'always followed by a line break
LineBreakTags = Array("br", "br/", "br /") 'always lead & followed by a line break
a = CleanOf(HTML)
TabsNo = -1
i = 1
l = Len(a)
Do While i < l
If Mid(a, i, 2) = "</" Then
tag = Mid(a, i + 2, InStr(i + 2, a, ">") - i - 2)
If Not IsInArray(tag, InlineClosingTags) Or Mid(a, i - 1, 1) = ">" Then
tabs = Chr(10) & Filler(TabsNo, Chr(9))
a = Left(a, i - 1) & tabs & Right(a, Len(a) - i + 1)
l = Len(a)
i = i + Len(tabs)
End If
TabsNo = TabsNo - 1
Else
Select Case Mid(a, i, 1)
Case "<"
tag = Mid(a, i + 1, InStr(i + 1, a, ">") - i - 1)
If Not IsInArray(tag, InlineTags) Then
TabsNo = TabsNo + 1
If TabsNo > MaxTabs Then MaxTabs = TabsNo
If i > 1 Then tabs = Chr(10) & Filler(TabsNo, Chr(9)) Else tabs = Filler(TabsNo, Chr(9))
' tabs = tabs & Filler(TabsNo, Chr(9))
a = Left(a, i - 1) & tabs & Right(a, Len(a) - i + 1)
l = Len(a)
i = i + Len(tabs)
If IsInArray(tag, LineBreakTags) Then TabsNo = TabsNo - 1
End If
Case ">"
tag = Mid(a, InStrRev(a, "<", i) + 1, i - InStrRev(a, "<", i) - 1)
If Not IsInArray(tag, InlineClosingTags) Then
tabs = Chr(10) & Filler(TabsNo + 1, Chr(9))
a = Left(a, i) & tabs & Right(a, Len(a) - i)
End If
Case Chr(10)
If Mid(a, i + 1, 1) <> Chr(9) And Mid(a, i + 1, 1) <> "<" Then
tabs = Chr(10) & Filler(TabsNo + 1, Chr(9))
a = Left(a, i) & tabs & Right(a, Len(a) - i)
l = Len(a)
i = i + Len(tabs)
End If
End Select
End If
i = i + 1
Loop
For TabsNo = MaxTabs To 0 Step -1
a = Replace(a, Chr(10) & Filler(TabsNo, Chr(9)) & Chr(10), Chr(10))
Next
ReadableHTML = treatInlineTags(a, False)
End Function
Which uses these helping functions:
Function treatInlineTags(a As String, HideFlag As Boolean)
'Hides/unhides inline tags from CleanOf
If HideFlag Then
For i = LBound(InlineTags) To UBound(InlineTags)
a = Replace(a, "<" & InlineTags(i) & " ", "|" & InlineTags(i) & "¦")
a = Replace(a, "<" & InlineTags(i) & ">", "|" & InlineTags(i) & "|")
a = Replace(a, "</" & InlineTags(i) & ">", "|/" & InlineTags(i) & "|")
Next i
Else
For i = LBound(InlineTags) To UBound(InlineTags)
a = Replace(a, "|" & InlineTags(i) & "¦", "<" & InlineTags(i) & " ")
a = Replace(a, "|" & InlineTags(i) & "|", "<" & InlineTags(i) & ">")
a = Replace(a, "|/" & InlineTags(i) & "|", "</" & InlineTags(i) & ">")
Next i
End If
treatInlineTags = a
End Function
Function IsInArray(a As String, Arr As Variant) As Boolean
Dim i As Long
For i = LBound(Arr) To UBound(Arr)
IsInArray = a = Arr(i)
If IsInArray Then Exit Function
Next i
End Function
Function CleanOf(a As String) As String
'Removes unwanted spaces between tags
Dim i As Long, b As Boolean, l As Long
a = Replace(a, Chr(13), "")
a = Replace(a, Chr(10), "")
a = treatInlineTags(a, True)
For i = 1 To Len(a)
Select Case Mid(a, i, 1)
Case ">", "<"
If i - l > 1 And l > 0 Then a = Left(a, l) & Right(a, Len(a) - i + 1)
If i > 1 Then l = i
If l > 0 Then b = True
Case Is <> " "
b = False
l = 0
End Select
Next i
CleanOf = a
End Function
Function Filler(n As Long, Optional Str As String = "0") As String
If n > 0 Then Filler = Replace(Space$(n), " ", Str)
End Function
To test it:
Sub test()
Dim a As String, b As String
a = "<div class=""myclass""> " & Chr(13) & _
"<ul><li>A</li> " & Chr(13) & _
"<li>B</li><li>C</li> " & _
"</ul></div> <!-- just a comment -->" & _
"<h2 class=""mytitle"">a title: inline and " & _
"followed by a line break</h2>" & _
"<div><ul><li><i class=""myitalic"">italic " & _
"content: inline and NOT followed by a line break</i>" & _
"</li></ul></div>"
b = "<li><i class=""mylist""></i>a list <ul>" & _
"<li>element 1</li><li>element 2</li><li>element 3</li></ul> " & _
"</li><li>This <b>is bold</b> in an element list " & _
"<a href=""#mydestination"">""with an href"" " & _
"</a></li>"
Debug.Print Chr(10) & "Test1 - input:" & Chr(10) & a
Debug.Print Chr(10) & "Test1 - output:" & Chr(10) & ReadableHTML(a)
Debug.Print Chr(10) & "Test2 - input:" & Chr(10) & b
Debug.Print Chr(10) & "Test2 - output:" & Chr(10) & ReadableHTML(b)
End Sub
Related
Because I didn't find a proper solution, I developed an own code to convert an excel range into an html table. The result is a string and can be used within a mail for example.
The following code considers: cell dimension, font-family, -style, -size and alignment, border-style and -color (top, right, bottom, left) and interior-color
Function convertRangeToHtml(rng As Range) As String
Dim r As Range
Dim c As Range
Dim strHtml As String
strHtml = "<table style=" & Chr(34) & "border-collapse: collapse;" & Chr(34) & " > " & vbNewLine & "<tbody>"
For Each r In rng.Rows
strHtml = strHtml & vbTab & "<tr>" & vbNewLine
For Each c In r.Columns
strHtml = strHtml & vbTab & vbTab & "<td " & getCellDimension(c) & " style=" & Chr(34) & getFontStyle(c) & " " & getBorder(c) & " " & getInteriorColor(c) & Chr(34) & ">" & Trim(c.Text) & "</td>" & vbNewLine
Next c
strHtml = strHtml & vbTab & "</tr>" & vbNewLine
Next r
strHtml = strHtml & "</table>" & vbNewLine & "</tbody>"
Debug.Print strHtml
convertRangeToHtml = strHtml
End Function
Function getInteriorColor(r As Range) As String
getInteriorColor = "background-color: rgb(" & color2rgb(r.DisplayFormat.Interior.Color) & "); "
End Function
Function getBorder(r As Range) As String
Dim varBorderSytle As Variant
varBorderSytle = "border-style: "
Dim varBorderWidth As Variant
varBorderWidth = "border-width: "
Dim varBorderColor As Variant
varBorderColor = "border-color: "
For Each b In Array(8, 10, 9, 7)
Select Case r.Borders(b).LineStyle
Case -4115
varBorderSytle = varBorderSytle & "dashed "
Case -4142
varBorderSytle = varBorderSytle & "none "
Case 1
varBorderSytle = varBorderSytle & "solid "
Case -4118
varBorderSytle = varBorderSytle & "dotted "
Case Else
varBorderSytle = varBorderSytle & "solid "
End Select
Select Case r.Borders(b).Weight
Case 1
varBorderWidth = varBorderWidth & "1px "
Case -4138
varBorderWidth = varBorderWidth & "2px "
Case 4
varBorderWidth = varBorderWidth & "3px "
Case 2
varBorderWidth = varBorderWidth & "1px "
Case Else
varBorderWidth = varBorderWidth & "1px "
End Select
varBorderColor = varBorderColor & "rgb(" & color2rgb(r.Borders(b).Color) & ") "
Next b
varBorderSytle = varBorderSytle & ";"
varBorderWidth = varBorderWidth & ";"
varBorderColor = varBorderColor & ";"
getBorder = varBorderSytle & " " & varBorderWidth & " " & varBorderColor
End Function
Function getCellDimension(r As Range) As String
getCellDimension = "width=" & Chr(34) & r.Width * 96 / 72 & "" & Chr(34) & " height=" & Chr(34) & r.Height * 96 / 72 & "" & Chr(34)
End Function
Function getFontStyle(r As Range) As String
Dim varFontColor As Variant
Dim varFontName As Variant
Dim varFontStyle As Variant
Dim varFontSize As Variant
Dim varFontBold As Variant
Dim varFontItalic As Variant
Dim varTextAlign As Variant
varFontColor = "color: rgb(" & color2rgb(r.DisplayFormat.Font.Color) & "); "
varFontName = "font-family: " & r.Font.Name & "; "
varFontSize = "font-size: " & r.DisplayFormat.Font.Size & "pt; "
varFontBold = "font-weight: normal; "
If r.DisplayFormat.Font.Bold Then varFontBold = "font-weight: bold; "
varFontStyle = "font-style: normal; "
If r.DisplayFormat.Font.Italic Then varFontStyle = "font-style: italic; "
Select Case r.HorizontalAlignment
Case -4131
varTextAlign = "text-align: left; "
Case -4152
varTextAlign = "text-align: right; "
Case -4108
varTextAlign = "text-align: center; "
Case -4130
varTextAlign = "text-align: justify; "
Case 1
Select Case Application.Evaluate("TYPE(" & r.Address(0, 0, external:=True) & ")")
Case 1
varTextAlign = "text-align: right; "
Case 2
varTextAlign = "text-align: left; "
Case 4, 16
varTextAlign = "text-align: center; "
Case Else
varTextAlign = "text-align: start; "
End Select
End Select
Dim strCSS As String
strCSS = varFontColor & varFontName & varFontSize & varFontBold & varFontStyle & varTextAlign
getFontStyle = strCSS
End Function
Function color2rgb(varColor As Variant) As String
color2rgb = Format((varColor Mod 256), "00") & ", " & Format(((varColor \ 256) Mod 256), "00") & ", " & Format((varColor \ 65536), "00")
End Function
Additionally I would suggest to add a css-style wihtin the final html-file:
td {
padding:1pt 4pt 1pt 4pt;
}
I have a string in this format similar to the json. Generated by the following code:
str = "{"
str &= Chr(34) & "code" & Chr(34) + ":" & Chr(34) & "0000001" & Chr(34)
str &= Chr(34) & "name" & Chr(34) + ":" & Chr(34) & "product 1" & Chr(34)
str &= Chr(34) & "value" & Chr(34) + ":" & Chr(34) & "150.00" & Chr(34)
str &= "}"
I just need to get the value after the code, name and value.
I cannot find an effective method to do this, as I will have to generalize to more terms later. How can I do this without transforming to JSON?
The code snippet you provide produces this string:
{"code":"0000001""name":"product 1""value":"150.00"}
Assuming you really are using VB6 to process this string, the following code breaks out the values:
Private Sub Test(ByVal str As String)
Dim groups As Variant
Dim values As Variant
Dim i As Integer
str = Replace(str, "{", "")
str = Replace(str, "}", "")
str = Replace(str, """""", ",")
str = Replace(str, """", "")
groups = Split(str, ",")
For i = LBound(groups) To UBound(groups)
values = Split(groups(i), ":")
Debug.Print values(1)
Next
End Sub
Something like this should help (untested):
colon% = 0
Dim completed as Boolean
while completed = false
colon% = InStr(colon% + 1, str, ":")
If colon% > 0 Then
value$ = Mid$(str, colon + 1, InStr(colon% + 2, str, Chr(34)) - colon%)
' ... do whatever with value$ here...
Else
completed = true
End If
Wend
I am trying to convert Excel cells to JSON data but not able to implement correct logic.
Excel data is as follow
and excepted result is as follows
{
'pr1' : [ { 'hw' : ['LC', 'Repl']},
{ 'web' : ['LC', 'Repl']}
]
}
I have written below code but it is not working as expected.
For i = 1 To 546
If pro <> Cells(i, 1).Value Then
oFile.writeline ""
oFile.write '" + Cells(i, 1).Value + " ':{ '" + Cells(i, 2).Value + "' : ' " + Cells(i, 3).Value + "', "
Else
If pro = Cells(i, 1).Value Then
If opt1 <> Cells(i, 2).Value Then
oFile.writeline " , '" + Cells(i, 2).Value + "' : " + ",'" + Cells(i, 3).Value + "'"
Else
oFile.write '" + Cells(i, 3).Value + " ', "
oFile.write ""
End If
oFile.write " "
End If
oFile.write " "
End If
If pro = Cells(i + 1, 1).Value And opt1 <> Cells(i + 1, 2).Value Then
oFile.write " } "
End If
If pro <> Cells(i + 1, 1).Value And opt1 <> Cells(i + 1, 2).Value Then
oFile.write " "
End If
pro = Cells(i, 1).Value
opt1 = Cells(i, 2).Value
opt2 = Cells(i, 3).Value
Next i
use Dictionary object to store :
unique column A values as keys and a new dictionary as their item
for each key, the dictionary item will store column B values as unique keys and the combination of all column C values as their item
as follows:
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim key As Variant, key2 As Variant
Dim cel As Range
With Worksheets("MySheetName") ' change "MySheetName" to your actual sheet name
For Each cel In .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).SpecialCells(xlCellTypeConstants)
key = cel.Value: key2 = cel.Offset(, 1).Value
If Not dict.exists(key) Then dict.Add key, CreateObject("Scripting.Dictionary")
dict(key).Item(key2) = dict(key).Item(key2) & "'" & cel.Offset(, 2).Value & "',"
Next
End With
' build the JSON string for each key (i.e. only "pr1" in your example)
Dim s As String
For Each key In dict.keys
s = String(4, " ") & "{" & vbCrLf & String(4, " ") & "'" & key & "' : [ "
For Each key2 In dict(key)
s = s & "{ '" & key2 & "' : [" & Left(dict(key)(key2), Len(dict(key)(key2)) - 1) & "]}," & vbCrLf & String(12, " ")
Next
s = Left$(s, Len(s) - 15) & vbCrLf & String(4, " ") & "]" & vbCrLf & String(3, " ") & "}"
Debug.Print s
Next
I am giving a spreedsheet and I need to convert into JSON.
I have the following spreadsheet as so:
In essence, I'd need to convert into like this:
{ "CompanyA": {
"Products": ["Beds", "Knifes", "Spoons"]
}, "CompanyB": {
"Products": ["Beds", "Knifes", "Spoons"],
"Sites": ["West Coast", "East Coast"]
}, "CompanyC": {
"Office": ["Los Angeles"]
}}
I tried looking at online sources, but I haven't got a good solution to what I am looking for
Here's some basic code which should point you to the right direction.
I have commented it as much as possible.
Sub GetJSONOutput()
Dim wks As Worksheet: Set wks = ActiveSheet
Dim lngLastRow As Long, i As Long, j As Long, k As Long
Dim blFirstRow As Boolean
Dim strOut As String
lngLastRow = wks.Cells.Find("*", wks.Cells(1, 1), , , , xlPrevious).Row
k = 1
For i = 1 To lngLastRow
'\\ First Element - Column A
'\\ Check for first line and build beginning style
If Len(wks.Cells(i, 1).Value) > 0 Then
If blFirstRow = False Then
strOut = "{ """ & wks.Cells(i, 1).Value & """: {"
blFirstRow = True
Else '\\ Rest follow the same style
strOut = "}, """ & wks.Cells(i, 1).Value & """: {"
End If
wks.Cells(k, 4).Value = strOut: k = k + 1 '--> Output Column D
End If
'\\ Middle element - Column B
If Len(wks.Cells(i, 2).Value) > 0 Then strbase = " """ & wks.Cells(i, 2).Value & """: ["
If Len(wks.Cells(i, 3).Value) > 0 Then
'\\ Now we have Middle element then we need to loop through all elements under it!
'\\ Last Element - Column C
If Len(wks.Cells(i + 1, 3).Value) > 0 Then
strAppend = ""
For j = i To wks.Cells(i, 3).End(xlDown).Row
strAppend = strAppend & "|" & wks.Cells(j, 3).Value
Next j
strOut = strbase & """" & Replace(Mid(strAppend, 2, Len(strAppend)), "|", Chr(34) & ", " & Chr(34)) & """]"
wks.Cells(k, 4).Value = strOut: k = k + 1 '--> Output Column D
i = j - 1
Else
strOut = strbase & """" & wks.Cells(i, 3).Value & """]"
wks.Cells(k, 4).Value = strOut: k = k + 1 '--> Output Column D
End If
End If
'\\ Complete output by outputting the last closing brackets
If i = lngLastRow Then
strOut = "}}"
wks.Cells(k, 4).Value = strOut '--> Output Column D
End If
Next i
End Sub
Sub ConvertToJSONText()
Dim Sht As Worksheet
Set Sht = Worksheets("Sheet1")
Dim a As Integer
Dim lstA
Dim lstB
Dim lstC
a = 0
Dim myJsonText
myJsonText = "{"
Do While True
a = a + 1
If Sht.Range("a" & a).Value = "" And Sht.Range("b" & a).Value = "" And Sht.Range("c" & a).Value = "" Then
Exit Do
End If
If Sht.Range("a" & a).Value <> "" And Sht.Range("b" & a).Value = "" And Sht.Range("c" & a).Value = "" Then
If lstB <> "" Then myJsonText = myJsonText & "]"
If lstA <> "" Then myJsonText = myJsonText & "},"
lstA = Sht.Range("a" & a).Value
lstB = ""
lstC = ""
myJsonText = myJsonText & """" & lstA & """: {"
End If
If Sht.Range("a" & a).Value = "" And Sht.Range("b" & a).Value <> "" And Sht.Range("c" & a).Value = "" Then
If lstB <> "" Then myJsonText = myJsonText & "]"
lstB = Sht.Range("B" & a).Value
lstC = ""
myJsonText = myJsonText & """" & lstB & """: ["
End If
If Sht.Range("a" & a).Value = "" And Sht.Range("b" & a).Value = "" And Sht.Range("c" & a).Value <> "" Then
If lstC <> "" Then myJsonText = myJsonText & ","
lstC = Sht.Range("C" & a).Value
myJsonText = myJsonText & """" & lstC & """"
End If
Loop
If lstB <> "" Then myJsonText = myJsonText & "]"
myJsonText = myJsonText & "}"
End Sub
I am trying to replace a word in a string. The below code does the replacing job, but it also replaces partial match which I don't want it to do.
If InStr(inputString, "North") Then
inputString = Replace(inputString, "North", "N")
End If
This code replaces north with N, which is great, but it also replaces Northern with Nern, which I don't want. How can I compare only the whole word?
In php it's == but I am not sure in VBA, by the way I am using this in MS Access VBA.
you could go like this
If InStr(inputString, "North") Then inputString = Trim(Replace(Replace(Replace(inputString & " ", " North ", " North "), " North ", " N"), " ", " "))
which is the "contraction" of
If InStr(inputString, "North") Then
inputString = inputString & " " '<-- add a space at the end to catch "North" should it be the last "word" in a possible "...North" string
inputString = Replace(inputString, " North ", " North ") '<-- double spaces before and after any " North " occurrence, necessary for subsequent statement to properly work should there be more than one " North " occurrence
inputString = Replace(inputString, " North ", " N") '<-- make the "core" replacement
inputString = Replace(Replace(inputString, " North ", " N"), " ", " ") '<-- make all double spaces occurrences as single ones
End If
Here is a solution to what you asked
Public Function WordMatch(ByVal Text As String, ByVal Word As String) As Boolean
Dim RightChar As String
Dim LeftChar As String
Dim IStart As Integer
Dim IEnd As Integer
Dim Flag As Boolean
Dim Alphabet As String
Alphabet = "abcdefghijklmnopqrstuvwxyz"
Flag = True
IStart = InStr(Text, Word)
If Not (IStart > 0) Then
Flag = False
Else
IEnd = IStart + Len(Word) - 1
If (IStart = 1 And IEnd = 1) Then GoTo WordMatched
If IStart > 1 Then
LeftChar = Mid(Text, IStart - 1, 1)
'LeftChar = Mid(Text, IStart - 1 - 3, 4)
'MsgBox "L'" & LeftChar & "' - " & Text & " - " & Word
If InStr(Alphabet, LeftChar) > 0 Then
Flag = False
End If
End If
If (IEnd < Len(Text)) Then
RightChar = Mid(Text, IEnd + 1, 1)
'RightChar = Mid(Text, IEnd + 1, 4)
'MsgBox "R'" & RightChar & "' - " & Text & " - " & Word
If InStr(Alphabet, RightChar) > 0 Then
Flag = False
End If
End If
'End If
End If
WordMatched:
WordMatch = Flag
End Function