我正在开发一个ASP经典项目,我已经实现了here的JScript JSON类.它能够与VBScript和JScript互操作,几乎完全是json.org提供的代码.我需要使用VBScript来完成这个项目.我的团队经理.
它在ASP中定义的基元和类上非常有效.但是我需要Dictionary对象,据我所知,这些对象只能通过COM interop获得. (通过Server.CreateObject(“Scripting.Dictionary”))我有以下代表产品的类:(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
%>
Specs属性是键:值对的字典.这是我如何序列化它:(product.asp)
<%
dim oProd
set oProd = new ProductInfo
' ... fill in properties
' ... output appropriate headers and stuff
Response.write( JSON.stringify( oProd ) )
%>
当我将ProductInfo的实例传递给JSON.Stringify时(如上所示),我得到如下内容:
{
"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": {}
}
如您所见,Specs属性是一个空对象.我相信JSON stringify方法知道Specs属性是一个对象,因此它将{}附加到字符串化输出周围的JSON字符串.在这种情况下,这是一个空字符串.我希望它展示的不是一个空的对象.见下文:
"Specs": {
"foo":"bar",
"baz":1,
"etc":"..."
}
我相信JSON库的问题区域在这里:(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);
}
}
}
我假设上面代码的问题是它假定所有对象都继承自Object类. (提供hasOwnProperty的那个)但是我认为COM对象可能不会从Object类继承 – 或者至少是相同的Object类.或者至少不要在它们上面实现所需的任何接口.
更新:虽然我认为这个问题无法解决 – 我希望某种Web客户端可以请求(通过http)此对象的JSON表示或此对象的集合.
tl; dr问题:我应该怎么做才能使Scripting.Dictionary能够正确输出为JSON而不是失败并返回一个空字符串?我是否需要’重新发明*’并在VBScript中编写我自己的Dictionary类,它在ASP中作为普通对象?
解决方法:
构造中的Javascript for …(在您引用的JSON序列化程序中使用)仅适用于本机JS对象.要枚举Scripting.Dictionary的键,您需要使用Enumerator对象,它将枚举Dictionary的键.
现在,通过检查每个属性上是否存在toJSON方法,JSON.stringify方法有一种允许自定义序列化的好方法.不幸的是,您无法像在原生JS对象上那样在现有COM对象上添加新方法,因此这是不行的.
然后是自定义字符串化函数,可以作为第二个参数传递给stringify方法调用.即使对于每个嵌套对象,也会为需要进行字符串化的每个对象调用该函数.我认为可以在这里使用.
一个问题是(AFAIK)JScript无法区分VBScript类型.对于JScript,任何COM或VBScript对象都具有typeof ===’object’.我知道获取该信息的唯一方法是定义将返回类型名称的VBS函数.
由于经典ASP文件的执行顺序如下:
>< script>使用非默认脚本语言的块(在您的情况下,JScript)
>< script>使用默认脚本语言的块(在您的情况下,VBScript)
><%...%>块,使用默认的脚本语言(在您的情况下,VBScript)
以下方法可行 – 但仅当JSON.stringify调用在<%...%>内完成时括号,因为这是JScript和VBScript< script>的唯一时间.部分都将被解析和执行.
最后的函数调用是这样的:
<%
Response.Write JSON.stringify(oProd, vbsStringifier)
%>
为了让JScript检查COM对象的类型,我们定义了一个VBSTypeName函数:
<script language="VBScript" runat="server">
Function VBSTypeName(Obj)
VBSTypeName = TypeName(Obj)
End Function
</script>
在这里,我们将vbsStringifier的完整实现作为第二个参数传递给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>
当然,在脚本引擎之间来回切换效率不高(即从JS调用VBS函数,反之亦然),因此您可能希望尽量将其保持在最低限度.
另请注意,由于我的计算机上不再有IIS,因此无法对此进行测试.基本原理应该有效,我不是100%肯定从VBScript传递JScript函数引用的可能性.您可能必须为JScript中的JSON.stringify调用编写一个小的自定义包装函数:
<script runat="server" language="JScript">
function JSONStringify(object) {
return JSON.stringify(object, vbsStringifier);
}
</script>
之后你可以简单地调整VBScript调用:
<%
Response.Write JSONStringify(oProd)
%>