2
votes

I'm reading the source code of VBA-JSON, which is located at:

https://github.com/VBA-tools/VBA-JSON/blob/master/JsonConverter.bas

In the function that dumps JSON objects (starting with "{" char) into memory:

Private Function json_ParseObject(json_String As String, ByRef json_Index As Long) As Dictionary
Dim json_Key As String
Dim json_NextChar As String

Set json_ParseObject = New Dictionary
json_SkipSpaces json_String, json_Index
If VBA.Mid$(json_String, json_Index, 1) <> "{" Then
    Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '{'")
Else
    json_Index = json_Index + 1

    Do
        json_SkipSpaces json_String, json_Index
        If VBA.Mid$(json_String, json_Index, 1) = "}" Then
            json_Index = json_Index + 1
            Exit Function
        ElseIf VBA.Mid$(json_String, json_Index, 1) = "," Then
            json_Index = json_Index + 1
            json_SkipSpaces json_String, json_Index
        End If

        json_Key = json_ParseKey(json_String, json_Index)
        json_NextChar = json_Peek(json_String, json_Index)
        If json_NextChar = "[" Or json_NextChar = "{" Then
            Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)
        Else
            json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)
        End If
    Loop
End If
End Function

What is the difference between:

Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)

And the following one:

json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)

I searched in SO and found this post, which does not dispel the confusion:

What does the keyword Set actually do in VBA?

From reading the code, I understand that if the parser reads a "{" or "[", then it needs to use a Dictionary to hold whatever between "{" and "}", and a Collection to hold whatever between "[" and "]", and if the parser reads something other than those two, then it is just a value, could be Boolean or String or Integer, etc.

What I don't get is the difference between Set and Assignment. "Set" will copy the address of the return variable of json_ParseValue, and assignment simply makes another copy of the return variable. Is it because that in the first case, there is further requirement to modify the returned object so it has to be passed by address (like in C++ we use &)?

2

2 Answers

4
votes

As the linked Q&A says, Set is used in VBA to assign an object reference. The Let keyword can be used to assign values, but the Let keyword is pretty much obsolete/redundant.

This is why you have these keywords in property mutator definitions though:

Private mSomething As Variant

Public Property Let Something(ByVal value As Variant)
    mSomething = value ' value is a primitive
End Property

Public Property Set Something(ByVal value As Variant)
    Set mSomething = value ' value is an object reference
End Property

So far, so good. The difference between this:

Set json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)

And this:

json_ParseObject.Item(json_Key) = json_ParseValue(json_String, json_Index)

Is that because json_ParseValue returns a Variant that can hold anything including an object reference or a simple value, then it needs to use the Set keyword when assigning a reference, and leave it out (or use the Let keyword) when assigning a value.


It's impossible to tell without looking at the implementation details and seeing exactly what is being returned, but it's also possible that a Let assignment could be assigning to a default member - and that default member call is implicit, and bloody confusing indeed.

It's exactly the same as when you're assigning a cell value in Excel VBA:

Dim r As Range
Set r = ActiveSheet.Range("A1")

vs

Dim r As Double
r = ActiveSheet.Range("A1") 'implicit call to ActiveSheet.Range("A1").[_Default]

The two instructions look different only because one has a Set keyword and the other doesn't, however the real difference is an implicit member call to a hidden property named [_Default], that has a VB_UserMemId = 0 attribute which makes that member the class' default member.

The confusion stems from implicit default member calls, which make the code say something, and actually do something else.

Rubberduck (an OSS VBIDE add-in project I manage) has a code inspection that locates implicit default member assignments in early-bound code:

Implicit default member assignment

Such assignments look like they are assigning an object variable to a value type on the surface, but they are actually assigning that object's default member, implicitly. Consider referring to the default member explicitly, for improved readability.

2
votes

The three types you are referring to are dictionary, collection, and array. Basically, any combination of those can make up a JSON structure.

Someone with more time will probably give a better answer, but the gist of it is (in my over-simplified interpretation),

  • Assigning a value to a variable makes a copy of that value.

  • Set is used for objects because instead of copying the object, it creates a reference to the object.

Generally, when you use Set to assign an object reference to a variable, no copy of the object is created for that variable. Instead, a reference to the object is created. More than one object variable can refer to the same object. Because such variables are references to the object rather than copies of the object, any change in the object is reflected in all variables that refer to it. However, when you use the New keyword in the Set statement, you are actually creating an instance of the object. (Source)


More Information: