I had tried the previous answer. Everything works fine until my data which extracted from server in the form of Json is giving me a key with multiple objects
Excel VBA: Parsed JSON Object Loop
something like this
{"messageCode":null,"responseStatus":"success","message":null,"resultObject":null,"resultObject2":[{"fxCcyPair":"USD"}, {"fxCcyPair":"EUR"},{"fxCcyPair":"JPY"},{"fxCcyPair":"GBD"}],"resultObject3":null,"resultObject4":null}
How can I get the value in "resultObject2"? as there is no key for me to refer and I am not able to loop the object out from it.
Public Sub InitScriptEngine()
Set ScriptEngine = New ScriptControl
ScriptEngine.Language = "JScript"
ScriptEngine.AddCode "function getProperty(jsonObj, propertyName) { return jsonObj[propertyName]; } "
ScriptEngine.AddCode "function getKeys(jsonObj) { var keys = new Array(); for (var i in jsonObj) { keys.push(i); } return keys; } "
ScriptEngine.AddCode "function getSentenceCount(){return obj.sentences.length;}"
ScriptEngine.AddCode "function getSentence(i){return obj.sentences[i];}"
End Sub
Public Function DecodeJsonString(ByVal JsonString As String)
Set DecodeJsonString = ScriptEngine.Eval("(" + JsonString + ")")
End Function
Public Function GetProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Variant
GetProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function
Public Function GetObjectProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Object
Set GetObjectProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function
Public Function GetKeys(ByVal JsonObject As Object) As String()
Dim Length As Integer
Dim KeysArray() As String
Dim KeysObject As Object
Dim index As Integer
Dim Key As Variant
Set KeysObject = ScriptEngine.Run("getKeys", JsonObject)
Length = GetProperty(KeysObject, "length")
ReDim KeysArray(Length - 1)
index = 0
For Each Key In KeysObject
KeysArray(index) = Key
Debug.Print Key
index = index + 1
Next
GetKeys = KeysArray
End Function
Thanks
This is a bit more manageable I think (based on S Meaden's answer at your linked question)
Sub TestJSONParsingWithVBACallByName()
Dim oScriptEngine As ScriptControl
Set oScriptEngine = New ScriptControl
oScriptEngine.Language = "JScript"
Dim objJSON As Object, arr As Object, el
'I pasted your JSON in A1 for testing...
Set objJSON = oScriptEngine.Eval("(" + Range("A1").Value + ")")
Debug.Print VBA.CallByName(objJSON, "responseStatus", VbGet)
'get the array associated with "resultObject2"
Set arr = VBA.CallByName(objJSON, "resultObject2", VbGet)
Debug.Print VBA.CallByName(arr, "length", VbGet) 'how many elements?
'loop over the array and print each element's "fxCcyPair" property
For Each el In arr
Debug.Print VBA.CallByName(el, "fxCcyPair", VbGet)
Next el
End Sub
Output:
success
4
USD
EUR
JPY
GBD
Related
The following code gives me the error system.argumentexception an element with the same key already exists. When I use in the the Friend Sub Test the following line instead: 'Dim str_rootdirectory As String = Directory.GetCurrentDirectory() ' "C:\TEMP" it works. Whats the difference?
My VB.NET code:
Public Class Form1
Public Sub recur_getdirectories(ByVal di As DirectoryInfo)
For Each directory As DirectoryInfo In di.GetDirectories()
'get each directory and call the module main to get the security info and write to json
Call Module1.Main(directory.FullName)
recur_getdirectories(directory)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim rootDirectory As String = TextBox1.Text.Trim()
Dim di As New DirectoryInfo(rootDirectory)
'get directories recursively and work with each of them
recur_getdirectories(di)
End Sub
End Class
Public Module RecursiveEnumerableExtensions
Iterator Function Traverse(Of T)(ByVal root As T, ByVal children As Func(Of T, IEnumerable(Of T)), ByVal Optional includeSelf As Boolean = True) As IEnumerable(Of T)
If includeSelf Then Yield root
Dim stack = New Stack(Of IEnumerator(Of T))()
Try
stack.Push(children(root).GetEnumerator())
While stack.Count <> 0
Dim enumerator = stack.Peek()
If Not enumerator.MoveNext() Then
stack.Pop()
enumerator.Dispose()
Else
Yield enumerator.Current
stack.Push(children(enumerator.Current).GetEnumerator())
End If
End While
Finally
For Each enumerator In stack
enumerator.Dispose()
Next
End Try
End Function
End Module
Public Module TestClass
Function GetFileSystemAccessRule(d As DirectoryInfo) As IEnumerable(Of FileSystemAccessRule)
Dim ds As DirectorySecurity = d.GetAccessControl()
Dim arrRules As AuthorizationRuleCollection = ds.GetAccessRules(True, True, GetType(Security.Principal.NTAccount))
For Each authorizationRule As FileSystemAccessRule In arrRules
Dim strAclIdentityReference As String = authorizationRule.IdentityReference.ToString()
Dim strInheritanceFlags As String = authorizationRule.InheritanceFlags.ToString()
Dim strAccessControlType As String = authorizationRule.AccessControlType.ToString()
Dim strFileSystemRights As String = authorizationRule.FileSystemRights.ToString()
Dim strIsInherited As String = authorizationRule.IsInherited.ToString()
Next
' This function should return the following values, because they should be mentoined in the JSON:
' IdentityReference = strAclIdentityReference
' InheritanceFlags = strInheritanceFlags
' AccessControlType = strAccessControlType
' FileSystemRights = strFileSystemRights
' IsInherited = strIsInherited
Return ds.GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount)).Cast(Of FileSystemAccessRule)()
End Function
Friend Sub Test(ByVal curDirectory As String)
'Dim str_rootdirectory As String = Directory.GetCurrentDirectory() ' "C:\TEMP"
Dim str_rootdirectory As String = curDirectory
Dim di As DirectoryInfo = New DirectoryInfo(str_rootdirectory)
Dim directoryQuery = RecursiveEnumerableExtensions.Traverse(di, Function(d) d.GetDirectories())
Dim list = directoryQuery.Select(
Function(d) New With {
.directory = d.FullName,
.permissions = {
GetFileSystemAccessRule(d).ToDictionary(Function(a) a.IdentityReference.ToString(), Function(a) a.FileSystemRights.ToString())
}
}
)
Dim json = JsonConvert.SerializeObject(list, Formatting.Indented)
File.WriteAllText("ABCD.json", json)
End Sub
End Module
Public Module Module1
Public Sub Main(ByVal curDirectory As String)
Console.WriteLine("Environment version: " & Environment.Version.ToString())
Console.WriteLine("Json.NET version: " & GetType(JsonSerializer).Assembly.FullName)
Console.WriteLine("")
Try
TestClass.Test(curDirectory)
Catch ex As Exception
Console.WriteLine("Unhandled exception: ")
Console.WriteLine(ex)
Throw
End Try
End Sub
End Module
My example folder structure:
Folder: "C:\Temp"
Permissions: SecurityGroup-A has Fullcontrol,
SecurityGroup-B has Modify permission
Folder: "C:\Temp\Folder_A"
Permissions: SecurityGroup-C has Fullcontrol
But this is only an example of two folders. In real, it will run over several hundered folders with sub-folders. Accordingly the JSON will extend.
My json output expectation:
[{
"directory": "C:\\TEMP",
"permissions": [{
"IdentityReference": "CONTOSO\\SecurityGroup-A",
"AccessControlType": "Allow",
"FileSystemRights": "FullControl",
"IsInherited": "TRUE"
}, {
"IdentityReference": "CONTOSO\\SecurityGroup-B",
"AccessControlType": "Allow",
"FileSystemRights": "Modify",
"IsInherited": "False"
}
]
}, {
"directory": "C:\\TEMP\\Folder_A",
"permissions": [{
"IdentityReference": "CONTOSO\\SecurityGroup-C",
"AccessControlType": "Allow",
"FileSystemRights": "Full Control",
"IsInherited": "False"
}
]
}
]
Your current JSON uses static property names for the [*].permissions[*] objects so there is no need to try to convert a list of them into a dictionary with variable key names via ToDictionary():
' This is not needed
.permissions = {
GetFileSystemAccessRule(d).ToDictionary(Function(a) a.IdentityReference.ToString(), Function(a) a.FileSystemRights.ToString())
}
Instead, convert each FileSystemAccessRule into some appropriate DTO for serialization. An anonymous type object works nicely for this purpose:
Public Module DirectoryExtensions
Function GetFileSystemAccessRules(d As DirectoryInfo) As IEnumerable(Of FileSystemAccessRule)
Dim ds As DirectorySecurity = d.GetAccessControl()
Dim arrRules As AuthorizationRuleCollection = ds.GetAccessRules(True, True, GetType(Security.Principal.NTAccount))
Return arrRules.Cast(Of FileSystemAccessRule)()
End Function
Public Function SerializeFileAccessRules(ByVal curDirectory As String, Optional ByVal formatting As Formatting = Formatting.Indented)
Dim di As DirectoryInfo = New DirectoryInfo(curDirectory)
Dim directoryQuery = RecursiveEnumerableExtensions.Traverse(di, Function(d) d.GetDirectories())
Dim list = directoryQuery.Select(
Function(d) New With {
.directory = d.FullName,
.permissions = GetFileSystemAccessRules(d).Select(
Function(a) New With {
.IdentityReference = a.IdentityReference.ToString(),
.AccessControlType = a.AccessControlType.ToString(),
.FileSystemRights = a.FileSystemRights.ToString(),
.IsInherited = a.IsInherited.ToString()
}
)
}
)
Return JsonConvert.SerializeObject(list, formatting)
End Function
End Module
Public Module RecursiveEnumerableExtensions
' Translated to vb.net from this answer https://stackoverflow.com/a/60997251/3744182
' To https://stackoverflow.com/questions/60994574/how-to-extract-all-values-for-all-jsonproperty-objects-with-a-specified-name-fro
' which was rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
' to "Efficient graph traversal with LINQ - eliminating recursion" https://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
Iterator Function Traverse(Of T)(ByVal root As T, ByVal children As Func(Of T, IEnumerable(Of T)), ByVal Optional includeSelf As Boolean = True) As IEnumerable(Of T)
If includeSelf Then Yield root
Dim stack = New Stack(Of IEnumerator(Of T))()
Try
stack.Push(children(root).GetEnumerator())
While stack.Count <> 0
Dim enumerator = stack.Peek()
If Not enumerator.MoveNext() Then
stack.Pop()
enumerator.Dispose()
Else
Yield enumerator.Current
stack.Push(children(enumerator.Current).GetEnumerator())
End If
End While
Finally
For Each enumerator In stack
enumerator.Dispose()
Next
End Try
End Function
End Module
Demo fiddle here (which unfortunately does not work on https://dotnetfiddle.net because of security restrictions on client code but should be runnable in full trust).
I'm receiving this JSON from my WebMethod:
{
"TableName": "myTable",
"Table": [
{
"ProdOrder": "245392",
"Item": "C01000FLS0300GF",
"Qty": 40
},
{
"ProdOrder": "245393",
"Item": "C01000FLS0400GF",
"Qty": 20
}
]
}
This is the WebMethod:
<WebMethod()>
Public Function MyWebMethod(strJSON As String) As String
Dim objJSON As Object = New JavaScriptSerializer().Deserialize(Of Object)(strJSON)
'Get TableName
Dim strTableName As String = objJSON("TableName")
'Get Data
Dim arrJSON As Object() = objJSON("Table")
For i As Integer = 0 To arrJSON.Length - 1
Dim ProdOrder As String = arrJSON(i)("ProdOrder")
Dim Item As String = arrJSON(i)("Item")
Dim Qty As String = arrJSON(i)("Qty")
Next
Return "OK"
End Function
Until here all is very simple and it works.
My question now is, how can I get the Names of the fields if I don't know them?
I mean, any way to get "ProdOrder", "Item" and "Qty"...
Using arrJSON(i)("ProdOrder") I get the Value. How can I get the title "ProdOrder"?
I found a way to do it:
Public Function MyWebMethod(strJSON As String) As String
Dim objJSON As Object = New JavaScriptSerializer().Deserialize(Of Object)(strJSON)
Dim strTableName, strFields, strValues As String
For Each JSONItem As KeyValuePair(Of String, Object) In objJSON
Select Case JSONItem.Key
Case "TableName"
'Get TableName
strTableName = JSONItem.Value
Case "Table"
'Get Data
For Each DataRecord As Object In JSONItem.Value
strFields = ""
strValues = ""
For Each DataField As KeyValuePair(Of String, Object) In DataRecord
strFields &= DataField.Key & ","
strValues &= DataField.Value & ","
Next
Next
End Select
Next
Return "OK"
End Function
I'm using the following code in VB.Net to fetch a set of time and temperature data in JSON format that I will end up charting. I want to convert the JSON data but I only get far as creating the jObject. After that, I get lost. I'll admit, I'm a bit of a newbie!
Imports System.Collections.Generic
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Partial Class public_html_JSON
Inherits System.Web.UI.Page
Public jResults As JObject
Public rawresp As String
Public strStartTime As String = ""
Public strEndTime As String = ""
Public rangeMinutes As Long
Public debugText As String
Private Sub form1_Load(sender As Object, e As EventArgs) Handles form1.Load
Dim request As HttpWebRequest
Dim response As HttpWebResponse = Nothing
Dim reader As StreamReader
Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim cstTime As DateTime = TimeZoneInfo.ConvertTimeFromUtc(DateAdd(DateInterval.Day, 0, DateAdd(DateInterval.Hour, 0, Now().ToUniversalTime)), cstZone)request =
Dim url As String = "https://[somesite]?method=queryList4Chart&device.id=17002&endTime=" + strEndTime + "&sensorNumber=-1&startTime=" + strStartTime
request = DirectCast(WebRequest.Create(url), HttpWebRequest)
response = DirectCast(request.GetResponse(), HttpWebResponse)
reader = New StreamReader(response.GetResponseStream())
rawresp = reader.ReadToEnd()
response = Nothing
jResults = JObject.Parse(rawresp)
But before I send it to the client-side, I want to:
Create a new JSON string using an "x, y" format with timeArray as "x" and dataArray[0] as
"y".
Reduce the number of data points by only keeping times with minutes divisible by 5. (i.e. 5,10,15,etc...)
Here is the data I want to transform:
"dataArray":[
[
{
"value":13.4
},
{
"value":13.2
},
{
"value":13.2
},
{
"value":13.5
}
],
[
{
"value":2.8
},
{
"value":2.8
},
{
"value":2.9
},
{
"value":3.0
}
]
],
"sensorArray":[
"1.TP1(℃)",
"2.TP2(℃)"
],
"timeArray":[
"2019/11/10 14:00:41",
"2019/11/10 14:05:40",
"2019/11/10 14:07:40",
"2019/11/10 14:10:40"
]
}
And I need it in this format:
[
{
"x":2019/11/10 14:00:00,
"y":13.4
},
{
"x":2019/11/10 14:05:00,
"y":13.2
},
{
"x":2019/11/10 14:10:00,
"y":13.5
}
]
How would I go about doing that?
Thanks to Craig's suggestion, here's the final, working code!
Imports System.Collections.Generic
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json
Partial Class KFPTempsV2
Inherits System.Web.UI.Page
Public strStartTime As String = ""
Public strEndTime As String = ""
Public rangeMinutes As Long
Public debugText As String
Public JSONxy As ArrayList = New ArrayList 'List of JSON strings
Private Sub form1_Load(sender As Object, e As EventArgs) Handles form1.Load
Dim request As HttpWebRequest
Dim response As HttpWebResponse = Nothing
Dim reader As StreamReader
Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim cstTime As DateTime = TimeZoneInfo.ConvertTimeFromUtc(DateAdd(DateInterval.Day, 0, DateAdd(DateInterval.Hour, 0, Now().ToUniversalTime)), cstZone)
Dim DeviceList As IEnumerable(Of TemperatureDevice) = GetTemperatureDevices()
Dim rawResp As String
Dim url As String = ""
Dim objXY As New List(Of List(Of XY))
rangeMinutes = 1440
strStartTime = DateAdd(DateInterval.Minute, -rangeMinutes, cstTime).ToString("MM/dd/yyyy\%20HH:mm:00")
strEndTime = cstTime.ToString("MM/dd/yyyy\%20HH:mm:ss")
'Get data from each device
For Each dv As TemperatureDevice In DeviceList
url = "https://www.[somesite].com/deviceDataAction.do?method=queryList4Chart&device.id=" & dv.ID & "&endTime=" + strEndTime + "&sensorNumber=-1&startTime=" + strStartTime
request = DirectCast(WebRequest.Create(url), HttpWebRequest)
response = DirectCast(request.GetResponse(), HttpWebResponse)
reader = New StreamReader(response.GetResponseStream())
rawResp = reader.ReadToEnd()
response = Nothing
Dim XYList As List(Of XY) = ConvertToXY(JsonConvert.DeserializeObject(Of JSONData)(rawResp))
JSONxy.Add(JsonConvert.SerializeObject(XYList))
objXY.Add(XYList) 'Store XYList for later use
Next
'Populate Current/H/L temperatures
Current1.InnerHtml = objXY(0)(objXY(0).Count - 1).y
High1.InnerHtml = MaxValue(objXY(0)).ToString
Low1.InnerHtml = MinValue(objXY(0)).ToString
Current2.InnerHtml = objXY(1)(objXY(1).Count - 1).y
High2.InnerHtml = MaxValue(objXY(1)).ToString
Low2.InnerHtml = MinValue(objXY(1)).ToString
Current3.InnerHtml = objXY(2)(objXY(2).Count - 1).y
High3.InnerHtml = MaxValue(objXY(2)).ToString
Low3.InnerHtml = MinValue(objXY(2)).ToString
Current4.InnerHtml = objXY(3)(objXY(3).Count - 1).y
High4.InnerHtml = MaxValue(objXY(2)).ToString
Low4.InnerHtml = MinValue(objXY(3)).ToString
End Sub
Function ConvertToXY(obj As JSONData) As List(Of XY)
Dim NewObj As List(Of XY) = New List(Of XY)
For i As Int16 = 0 To obj.DataArray(1).Length - 1
Dim oDate As DateTime = Convert.ToDateTime(obj.TimeArray(i))
Dim oNewDate As DateTime = New DateTime(oDate.Year, oDate.Month, oDate.Day, oDate.Hour, oDate.Minute, 0).AddMinutes(Math.Round(oDate.Second / 60))
If oNewDate.Minute Mod 5 = 0 Then
Dim objXY As XY = New XY
objXY.x = oNewDate.ToString("yyyy/MM/dd HH:mm:ss")
objXY.y = obj.DataArray(1)(i).Value
NewObj.Add(objXY)
End If
Next
Return NewObj
End Function
Function MinValue(oList As List(Of XY)) As String
Dim sglMin As Single = 200
For Each row As XY In oList
Try
If CSng(row.y) < sglMin Then
sglMin = CSng(row.y)
End If
Catch ex As Exception
End Try
Next
Return sglMin.ToString("F1")
End Function
Function MaxValue(oList As List(Of XY)) As String
Dim sglMax As Single = -200
For Each row As XY In oList
Try
If CSng(row.y) > sglMax Then
sglMax = CSng(row.y)
End If
Catch ex As Exception
End Try
Next
Return sglMax.ToString("F1")
End Function
Public Class JSONData
Public Property DataArray As DataArray()()
Public Property SensorArray As String()
Public Property TimeArray As String()
End Class
Public Class DataArray
Public Property Value As String
End Class
Class XY 'Important that xy field names be lowercase
Property x As String
Property y As String
End Class
Public Class TemperatureDevice
Public Name As String
Public ID As String
Public DatasetNum As Int16
Public Sub New()
End Sub
Public Sub New(ByVal _name As String,
ByVal _id As String,
ByVal _datasetNum As Int16
)
Name = _name
ID = _id
DatasetNum = _datasetNum
End Sub
End Class
Private Function GetTemperatureDevices() As IEnumerable(Of TemperatureDevice)
'Dataset choices TP1=0 Or TP2=1
Return New List(Of TemperatureDevice) From
{
New TemperatureDevice("Pump House", "17002", 1),
New TemperatureDevice("Planer", "7199", 1),
New TemperatureDevice("Sawmill", "7123", 1),
New TemperatureDevice("Wellons", "13293", 1)
}
End Function
End Class
I'm getting from the server a JSON string with the statuses of a particular actions. In this case it returns results for 2 actions.
For
ID: 551720
and
ID: 551721
String looks like this:
[{"ElectronicId":551720,"DocumentNr":"130/10/15",
"DocumentTypeId":1,"DocumentTypeName":"eInvoice",
"StatusId":30,"StatusName":"Sent","RecipientBusinessNumber":"0050960000",
"RecipientBusinessUnit":"","RecipientBusinessName":"Comp d.o.o.",
"Created":"2019-07-23T21:21:23.743","Updated":"2019-07-23T21:21:24.587",
"Sent":"2019-07-23T21:21:24.587","Delivered":null},
{"ElectronicId":551721,"DocumentNr":"130/10/15",
"DocumentTypeId":1,"DocumentTypeName":"eInvoice",
"StatusId":30,"StatusName":"Sent","RecipientBusinessNumber":"00509605454",
"RecipientBusinessUnit":"","RecipientBusinessName":"Comp d.o.o.",
"Created":"2019-07-23T21:23:05.887","Updated":"2019-07-23T21:23:07.043",
"Sent":"2019-07-23T21:23:07.043","Delivered":null}]
Sometimes it returns 1, sometimes 2, or maybe 20 statuses (different "ElectronicId")
How could I loop within JSON.
I have a code that works when I have only 1 response, but it doesn't work when I have more than 1.
Here is the code for 1 response:
Dim cJS As New clsJasonParser
cJS.InitScriptEngine
results = """""here goes the JSON string""""""
Set JsonObject = cJS.DecodeJsonString(CStr(result))
Debug.Print cJS.GetProperty(JsonObject, "ElectronicId")
Debug.Print cJS.GetProperty(JsonObject, "DocumentNr")
Debug.Print cJS.GetProperty(JsonObject, "DocumentTypeId")
Debug.Print cJS.GetProperty(JsonObject, "DocumentTypeName")
Debug.Print cJS.GetProperty(JsonObject, "StatusId")
Here is the code for the clsJasonParser bClass:
Option Explicit
Private ScriptEngine As ScriptControl
Public Sub InitScriptEngine()
Set ScriptEngine = New ScriptControl
ScriptEngine.Language = "JScript"
ScriptEngine.AddCode "function getProperty(jsonObj, propertyName) { return jsonObj[propertyName]; } "
ScriptEngine.AddCode "function getKeys(jsonObj) { var keys = new Array(); for (var i in jsonObj) { keys.push(i); } return keys; } "
End Sub
Public Function DecodeJsonString(ByVal JsonString As String)
Set DecodeJsonString = ScriptEngine.Eval("(" + JsonString + ")")
End Function
Public Function GetProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Variant
GetProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function
Public Function GetObjectProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Object
Set GetObjectProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function
I would use jsonconverter.bas to parse the json. After installing the code from that link in a standard module called JsonConverter, go to VBE > Tools > References > Add a reference to Microsoft Scripting Runtime.
Then I would dimension an array to hold the results. I would determine rows from the number of items in the json collection returned and the number of columns from the size of the first item dictionary. Loop the json object, and inner loop the dictionary keys of each dictionary in collection, and populate the array. Write the array out in one go at end.
Below, I am reading in the json string from cell A1 but you would replace that with your json source.
Option Explicit
Public Sub test()
Dim json As Object, r As Long, c As Long, headers()
Dim results(), ws As Worksheet, item As Object, key As Variant
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set json = JsonConverter.ParseJson(ws.[A1].Value) '<Reading json from cell. Returns collection
headers = json.item(1).keys 'each item in collection is a dictionary. Use .keys to get headers for results e.g. ElectronicId
ReDim results(1 To json.Count, 1 To UBound(headers) + 1)
For Each item In json 'loop json and populate results array
r = r + 1: c = 1
For Each key In item.keys
results(r, c) = item(key)
c = c + 1
Next
Next
With ws
.Cells(2, 1).Resize(1, UBound(headers) + 1) = headers
.Cells(3, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
End With
End Sub
I have this kind of table:
I need to get this JSON (of course order could be any, structure/tree is most important):
Data table can change, so serialization should be dynamic. I am working with vb.net and used this code:
Public Function GetJson() As String
Dim dt As New System.Data.DataTable
dt = CreateDataTable() 'here I retrive data from oracle DB
Dim serializer As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim packet As New List(Of Dictionary(Of String, Object))()
Dim row As Dictionary(Of String, Object) = Nothing
For Each dr As DataRow In dt.Rows
row = New Dictionary(Of String, Object)()
For Each dc As DataColumn In dt.Columns
row.Add(dc.ColumnName.Trim(), dr(dc))
Next
packet.Add(row)
Next
Return serializer.Serialize(packet)
End Function
But this code returns me bad json: [{"NAME":"city","PARENT":"address","VALUE":"has child"},{"NAME":"coordinates","PARENT":"address","VALUE":"has child"},{"NAME":"street","PARENT":"address","VALUE":"has child"}.......
Can someone help me out in here?
The 'Oh-no you didn't' version:
Public Function GetJson(ByVal dt As DataTable) As String
Return New JavaScriptSerializer().Serialize(From dr As DataRow In dt.Rows Select dt.Columns.Cast(Of DataColumn)().ToDictionary(Function(col) col.ColumnName, Function(col) dr(col)))
End Function
Here is my solution:
Public Function GetJson() As String
Dim dt As New System.Data.DataTable
dt = CreateDataTable() 'here I retrive data from oracle DB
Dim serializer As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim packet As New List(Of Dictionary(Of String, Object))()
Dim row As Dictionary(Of String, Object) = Nothing
Try
Dim result() As DataRow = dt.Select("Parent_ = 'main'")
Dim i As Integer
For i = 0 To result.GetUpperBound(0)
row = New Dictionary(Of String, Object)()
If UCase(result(i)(2)) <> "HAS CHILD" Then
row.Add(result(i)(0), result(i)(2))
Else
row.Add(result(i)(0), add_(dt, result(i)(0)))
End If
packet.Add(row)
Next i
Return serializer.Serialize(packet)
Catch ex As Exception
MsgBox(ex.ToString)
Me.Close()
End Try
End Function
Public Function add_(ByVal dt As System.Data.DataTable, ByVal parent_ As String) As Dictionary(Of String, Object)
Dim row As Dictionary(Of String, Object) = Nothing
Try
Dim result() As DataRow = dt.Select("Parent_ = '" & parent_ & "'")
Dim i As Integer
row = New Dictionary(Of String, Object)()
For i = 0 To result.GetUpperBound(0)
If UCase(result(i)(2)) <> "HAS CHILD" Then
row.Add(result(i)(0), result(i)(2))
Else
row.Add(result(i)(0), add_(dt, result(i)(0)))
End If
Next i
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Return row
End Function