JSON.Stringify not working in Scripting.Dictionary objects

I am working on an ASP classic project where I implemented the JScript JSON class found here . It is capable of interacting with both VBScript and JScript, and this is almost certainly the code presented in json.org . I have to use VBScript for this project by my team manager.

It works great on primitives and classes defined in ASP. But I need vocabulary objects, which, as far as I know, are accessible only through COM interaction. (via Server.CreateObject("Scripting.Dictionary") ) I have the following class, which is a product: (ProductInfo.class.asp)

 <% Class ProductInfo Public ID Public Category Public PriceUS Public PriceCA Public Name Public SKU Public Overview Public Features Public Specs End Class %> 

The Specs property is a dictionary of words: pairs of meanings. This is how I serialize it: (product.asp)

 <% dim oProd set oProd = new ProductInfo ' ... fill in properties ' ... output appropriate headers and stuff Response.write( JSON.stringify( oProd ) ) %> 

When I pass the ProductInfo instance to JSON.Stringify (as shown above), I get the following:

 { "id": "1547", "Category": { "id": 101, "Name": "Category Name", "AlternateName": "", "URL": "/category_name/", "ParentCategoryID": 21 }, "PriceUS": 9.99, "PriceCA": 11.99, "Name": "Product Name", "SKU": 3454536, "Overview": "Lorem Ipsum dolor sit amet..", "Features": "Lorem Ipsum dolor sit amet..", "Specs": {} } 

As you can see, the Specs property is an empty object. I believe the string JSON method knows that the Specs property is an object, so it adds {} to the JSON string around the string output. In this case, this is an empty string. What I expect is to show, however, not an empty object. See below:

 "Specs": { "foo":"bar", "baz":1, "etc":"..." } 

I believe the problem area of โ€‹โ€‹the JSON library is here: (json2.asp)

 // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } 

I maintain that the problem with the above code is that it assumes that all objects inherit from the Object class. (The one that hasOwnProperty provides). However, I believe that the likelihood that COM objects do not inherit from the Object class - or at least the same Object class. Or at least do not implement any interface necessary to execute for ... in on them.

Update: So far I feel that the question is impossible to answer; - I expect some web client to request (via http) a JSON representation of this object or a collection of this object.

tl; dr Question: What should I do so that Scripting.Dictionary can be correctly output as JSON instead of rejecting and returning only an empty string? Do I need to โ€œreinvent the wheelโ€ and write my own Dictionary class in VBScript that acts like a regular object in ASP?

+2
source share
3 answers

The Javascripts for...in construct (which is used in the JSON serializer you are referencing) only works with native JS objects. To enumerate the keys of Scripting.Dictionary s, you need to use an Enumerator object that will enumerate the keys of the Dictionary.

The JSON.stringify method JSON.stringify has a great way to enable custom serialization by checking for the toJSON method for each property. Unfortunately, you cannot use new methods for existing COM objects the way you can for your own JS objects, so this is not-go.

Then a user-defined function of the string dictionary is created, which can be passed as the second argument to the call to the stringify method. This function will be called for every object that should be wall-mounted, even for every nested object. I think that could be used here.

One problem is that (AFAIK) JScript cannot distinguish between VBScript types on its own. For JScript, any COM or VBScript object has typeof === 'object' . The only way to find out this information is to define a VBS function that will return the type name.

Since the execution order for classic ASP files is as follows:

  • <script> blocks with non-standard script languages โ€‹โ€‹(in your case, JScript)
  • <script> blocks the default script language (in your case, VBScript)
  • <% ... %> using the default script language (in your case, VBScript)

The following steps may work, but only when the JSON.stringify call JSON.stringify made in brackets <% ... %> , as this is the only time that both JScript and VBScript <script> sections have been parsed and executed.

The last function call will be as follows:

 <% Response.Write JSON.stringify(oProd, vbsStringifier) %> 

So that JScript can check the type of the COM object, we would define the VBSTypeName function:

 <script language="VBScript" runat="server"> Function VBSTypeName(Obj) VBSTypeName = TypeName(Obj) End Function </script> 

And here we have the full implementation of vbsStringifier, which is passed as the second parameter to JSON.stringify:

 <script language="JScript" runat="server"> function vbsStringifier(holder, key, value) { if (VBSTypeName(value) === 'Dictionary') { var result = '{'; for(var enr = new Enumerator(value); !enr.atEnd(); enr.moveNext()) { key = enr.item(); result += '"' + key + '": ' + JSON.stringify(value.Item(key)); } result += '}'; return result; } else { // return the value to let it be processed in the usual way return value; } } </script> 

Of course, switching between scripting mechanisms is not very efficient (i.e., calling a VBS function from JS and vice versa), so you probably want to try to minimize this.


Also note that I could not verify this, since I no longer have IIS on my machine. The basic principle should work, Im not 100% sure of the ability to pass a link to a JScript function from VBScript. You may need to write a small custom wrapper function to call JSON.stringify in JScript:

 <script runat="server" language="JScript"> function JSONStringify(object) { return JSON.stringify(object, vbsStringifier); } </script> 

after which you can simply configure the VBScript call:

 <% Response.Write JSONStringify(oProd) %> 
+4
source

I ended up writing a function for serializing a dictionary type. Unfortunately, you will have to go in and search for and replace with any unsuccessful serialization of dictionaries. ( {} ) I donโ€™t have time to find an automated way to do this. You can fork it on BitBucket .

 Function stringifyDictionary( key, value, sp ) dim str, val Select Case TypeName( sp ) Case "String" sp = vbCrLf & sp Case "Integer" sp = vbCrLf & Space(sp) Case Else sp = "" End Select If TypeName( value ) = "Dictionary" Then str = """" & key & """:{" & sp For Each k in value val = value.Item(k) If Not Right(str, 1+len(sp)) = "{" & sp And Not Right(str, 1+len(sp)) = "," & sp Then str = str & "," & sp End If str = str & """" & k & """: " If TypeName( val ) = "String" Then If val = "" Then str = str & "null" Else str = str & """" & escapeJSONString( val ) & """" End If Else str = str & CStr( val ) End If Next str = str & sp & "}" stringifyDictionary = str Else stringifyDictionary = value End If End Function Function escapeJSONString( str ) escapeJSONString = replace(replace(str, "\", "\\"), """", "\""") End Function 

This was written as a function to use with the JSON.stringify replace argument (2nd argument). However, you cannot pass a VBScript function as an argument. (From my experience) If you need to rewrite this function in JScript, you can use it when you call JSON.stringify to make sure the dictionaries actually display correctly. Read more about this at BitBucket . Here's how I implemented it:

 dim spaces: spaces = 2 dim prodJSON: prodJSON = JSON.stringify( oProduct, Nothing, spaces) prodJSON = replace(prodJSON, """Specs"": {}", stringifyDictionary("Specs", oProduct.Specs, spaces * 2)) 

Known Issues:

  • Closing } for Dictionary serialization will have the same amount of indentation as the properties contained in it. I did not have time to figure out how to deal with this without adding another argument that I do not want to do.
+1
source

JSON does not essentially encode any type information. What JSON allows you to represent is an arbitrary data structure that includes either an object or an array of values. Any such object can have an arbitrary number of named properties, so the names are strings and the values โ€‹โ€‹are null , true or false , numbers, strings, objects or arrays of values.

How such a data structure is implemented in any given language programming environment is your problem :-) For example, when de-serializing JSON in Java, you can use ArrayList instances for arrays, HashMap instances for objects, and native types for simpler values. However, perhaps you really want the objects to be a specific Java bean class. To do this, the JSON parser should somehow be guided by what objects need to be created. Exactly how this works depends on the JSON parser and its API.

(edit - when I said "no type information at all," I meant the meaning of "object", clear logics, strings and numbers have obvious types.)

0
source

Source: https://habr.com/ru/post/1302696/


All Articles