【发布时间】:2019-08-08 02:00:14
【问题描述】:
背景:我目前正在使用VBA-JSON 将 json 字符串解析为 VBA(Access)中的字典对象。这很慢,一个采样过程需要 18 秒。
在 VB.NET 中,JavaScriptSerializer Deserialize 方法需要 0.5 秒来处理相同的数据。
我希望通过 COM Interop 为我的 VBA 代码提供 VB.NET 方法的性能。但是 COM 不能传递通用对象,虽然我已经读到解决方案涉及封送处理,但我无法理解该选项。
当我手动生成一个 scripting.dictionary 类型并可以在 VBA 中使用时,我可以成功地从我的 VB.NET COM 类传递它。
Public Function GetData2() As Scripting.Dictionary
Dim dict As New Scripting.Dictionary
dict.Add("a", "Athens")
dict.Add("b", "Belgrade")
Return dict
End Function
但是 JavaScriptSerializer Deserialize 方法返回类型 IDictionary,而不是 scripting.dictionary。
所以我必须找到一种方法将 json 反序列化为 scripting.dictionary,或者将 IDictionary 转换为 scripting.dictionary。
我该怎么做?
鉴于我的总体目标,对替代方法有什么建议吗?
编辑。
该项目将专有的 REST API 用于会计系统。我想创建通用工具来简化和加速各种不同的任务,包括 Access、Excel、vbscript 等环境。API 设计人员想要的一切,但从通常对 REST API 编程不友好的工具开始。
用途可以从系统读取和写入数据,或将数据加载到另一个数据库,在 Excel 中创建自定义报告,导入订单等。
这是销售订单的一些 JSON。
{
"id": 7,
"orderNo": "0000102692",
"division": "000",
"location": "",
"profitCenter": "",
"invoiceNo": "",
"customer": {
"id": 1996,
"code": "ER118",
"customerNo": "ER118",
"name": "E R Partridge Inc"
},
"currency": null,
"status": "O",
"type": "O",
"hold": false,
"orderDate": "2015-02-13",
"invoiceDate": null,
"requiredDate": "2015-02-13",
"address": {
"id": 2045,
"type": "B",
"linkTable": "SORD",
"linkNo": "0000102692",
"shipId": "",
"name": "E R Partridge Inc",
"line1": "1531 St Jean Baptiste St",
"line2": "",
"line3": "",
"line4": "",
"city": "St Ulric",
"postalCode": "G0J 3H0",
"provState": "QC",
"country": "CAN",
"phone": {
"number": "4187370284",
"format": 1
},
"fax": {
"number": "",
"format": 1
},
"email": "van@erpart.com",
"website": "",
"shipCode": "",
"shipDescription": "",
"salesperson": {
"code": "",
"name": ""
},
"territory": {
"code": "",
"description": ""
},
"sellLevel": 1,
"glAccount": "41100",
"defaultWarehouse": "VA",
"created": "2014-08-26T11:44:57.930000",
"modified": "2015-02-16T09:30:08",
"contacts": [
{
"name": "Van Coon",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
},
{
"name": "",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
},
{
"name": "",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
}
],
"salesTaxes": [
{
"code": 1,
"exempt": ""
},
{
"code": 2,
"exempt": ""
},
{
"code": 0,
"exempt": ""
},
{
"code": 0,
"exempt": ""
}
]
},
"shippingAddress": {
"id": 2044,
"type": "S",
"linkTable": "SORD",
"linkNo": "SORD0000102692 S",
"shipId": "",
"name": "E R Partridge Inc",
"line1": "1531 St Jean Baptiste St",
"line2": "",
"line3": "",
"line4": "",
"city": "St Ulric",
"postalCode": "G0J 3H0",
"provState": "QC",
"country": "CAN",
"phone": {
"number": "4187370284",
"format": 1
},
"fax": {
"number": "",
"format": 1
},
"email": "",
"website": "",
"shipCode": "",
"shipDescription": "",
"salesperson": {
"code": "",
"name": ""
},
"territory": {
"code": "",
"description": ""
},
"sellLevel": 1,
"glAccount": "41100",
"defaultWarehouse": "VA",
"created": "2014-08-26T11:44:57.930000",
"modified": "2014-08-26T11:44:57.930000",
"contacts": [
{
"name": "Van Coon",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
},
{
"name": "",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
},
{
"name": "",
"email": "",
"phone": {
"number": "",
"format": 1
},
"fax": {
"number": "",
"format": 1
}
}
],
"salesTaxes": [
{
"code": 1,
"exempt": ""
},
{
"code": 2,
"exempt": ""
},
{
"code": 0,
"exempt": ""
},
{
"code": 0,
"exempt": ""
}
]
},
"contact": {
"name": "",
"email": "",
"phone": {
"number": "",
"format": 0
},
"fax": {
"number": "",
"format": 0
}
},
"customerPO": "",
"batchNo": 0,
"fob": "Your dock",
"referenceNo": "",
"shippingCarrier": "",
"shipDate": null,
"trackingNo": "",
"termsCode": "",
"termsText": "",
"freight": "41.95",
"taxes": [
{
"code": 1,
"name": "G.S.T.",
"shortName": "G.S.T.",
"rate": "5",
"exemptNo": "",
"total": "44.05"
},
{
"code": 2,
"name": "P.S.T.",
"shortName": "BC P.S.T.",
"rate": "7",
"exemptNo": "",
"total": "61.67"
},
{
"code": 0,
"name": "",
"shortName": "",
"rate": "0",
"exemptNo": "",
"total": 0
},
{
"code": 0,
"name": "",
"shortName": "",
"rate": "0",
"exemptNo": "",
"total": 0
}
],
"subtotal": "839",
"subtotalOrdered": "839",
"discount": "0",
"totalDiscount": "0",
"total": "986.67",
"totalOrdered": "986.67",
"grossProfit": "346.26",
"items": [
{
"id": 8,
"orderNo": "0000102692",
"sequence": 1,
"inventory": {
"id": 40,
"whse": "VA",
"partNo": "INSDB30",
"description": "InSpire Dumbbell 30"
},
"serials": null,
"whse": "VA",
"partNo": "INSDB30",
"description": "InSpire Dumbbell 30",
"comment": "",
"orderQty": "4",
"committedQty": "4",
"backorderQty": "0",
"retailPrice": "70",
"unitPrice": "70",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "INSPIRE",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "280",
"extendedPriceCommitted": "280",
"suppress": false
},
{
"id": 9,
"orderNo": "0000102692",
"sequence": 2,
"inventory": {
"id": 27,
"whse": "VA",
"partNo": "NATACCBAL",
"description": "National Accupressure Balls"
},
"serials": null,
"whse": "VA",
"partNo": "NATACCBAL",
"description": "National Accupressure Balls",
"comment": "",
"orderQty": "5",
"committedQty": "5",
"backorderQty": "0",
"retailPrice": "22",
"unitPrice": "22",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "NATPRO",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "110",
"extendedPriceCommitted": "110",
"suppress": false
},
{
"id": 10,
"orderNo": "0000102692",
"sequence": 3,
"inventory": {
"id": 33,
"whse": "VA",
"partNo": "SPAB",
"description": "Springfield Ab Toner"
},
"serials": null,
"whse": "VA",
"partNo": "SPAB",
"description": "Springfield Ab Toner",
"comment": "",
"orderQty": "1",
"committedQty": "1",
"backorderQty": "0",
"retailPrice": "45",
"unitPrice": "45",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "SPRFIT",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "45",
"extendedPriceCommitted": "45",
"suppress": false
},
{
"id": 11,
"orderNo": "0000102692",
"sequence": 4,
"inventory": {
"id": 46,
"whse": "VA",
"partNo": "INSDB50",
"description": "InSpire Dumbbell 50"
},
"serials": null,
"whse": "VA",
"partNo": "INSDB50",
"description": "InSpire Dumbbell 50",
"comment": "",
"orderQty": "2",
"committedQty": "2",
"backorderQty": "0",
"retailPrice": "118",
"unitPrice": "118",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "INSPIRE",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "236",
"extendedPriceCommitted": "236",
"suppress": false
},
{
"id": 12,
"orderNo": "0000102692",
"sequence": 5,
"inventory": {
"id": 42,
"whse": "VA",
"partNo": "INSDB15",
"description": "InSpire Dumbbell 15"
},
"serials": null,
"whse": "VA",
"partNo": "INSDB15",
"description": "InSpire Dumbbell 15",
"comment": "",
"orderQty": "3",
"committedQty": "3",
"backorderQty": "0",
"retailPrice": "34",
"unitPrice": "34",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "INSPIRE",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "102",
"extendedPriceCommitted": "102",
"suppress": false
},
{
"id": 13,
"orderNo": "0000102692",
"sequence": 6,
"inventory": {
"id": 9,
"whse": "VA",
"partNo": "INSWP50",
"description": "InSpire Weight Plate 50"
},
"serials": null,
"whse": "VA",
"partNo": "INSWP50",
"description": "InSpire Weight Plate 50",
"comment": "",
"orderQty": "1",
"committedQty": "1",
"backorderQty": "0",
"retailPrice": "66",
"unitPrice": "66",
"discountable": true,
"discountPct": "0",
"discountAmt": "0",
"taxFlags": [
true,
true,
false,
false
],
"sellMeasure": "EA",
"vendor": "INSPIRE",
"levyCode": "",
"requiredDate": "2015-08-26",
"extendedPriceOrdered": "66",
"extendedPriceCommitted": "66",
"suppress": false
}
],
"payments": [
],
"createdBy": "SS",
"modifiedBy": "SS",
"created": "2014-08-26T11:44:57.930000",
"modified": "2015-02-20T08:09:55",
"links": {
"notes": "https://localhost:10880/api/v2/companies/INSPIRE/sales/orders/7/notes/"
}
}
编辑 2
Erik A 的回答向我展示了如何按需或“流式传输”进行解析。只有当您请求元素时才会进行解析。
我想我误解了 VB.NET 如何反序列化 JSON 的本质。它必须做同样的事情。所以当我看到 500 毫秒而不是 18 秒时,我不知道我在看什么。我怀疑如果我在 VB.NET 中遍历反序列化的 json 并检查每个元素,则需要更长的时间。我用于性能测试的样本数据实际上是 124 个 JSON 样本的集合,其中一些包含更多的项目,因此需要 18 秒。 这是正确的吗?
Albert 的回答向我展示了我最初想做的事情,但无法让它发挥作用。很好的完整答案,我将在接下来深入研究。
【问题讨论】:
-
您可以将 .NET 的 IDictionary 转换为 .NET COM 对象中的 Scripting.Dictionary,因为它可以访问这两种类型(当然,您必须递归地执行此操作)。
-
你能分享你想做的操作,以及示例 JSON 数据吗? VBA-JSON 有其局限性,我一直在研究一种尚未完全测试但可能适合您的需求的替代方案。
-
但这有关系吗?您可以使用类型对象并将其传回。但是为什么不将 DAO 记录集传递给您的 .net 例程,并让 .net 代码将数据写到表中呢?目前尚不清楚您在何处/何时使用 json 数据,但我假设您有一个 json 字符串,并希望将该字符串展开到 Access 表中,对吗?编辑:您还可以从 .net 将集合作为您在 VBA 中定义的类型类传回。或者,您可以传回一种 iList,它是在 VBA 中使用的一种体面的数据类型。
-
为什么不让 vb.net 为您创建课程?在 vb.net 中创建一个类(然后删除所有文本。现在从文本文件中剪切 Jason,然后选择编辑->选择性粘贴,选择 json。VB.net 将为您生成基于该 Jason 的类。现在, 你可以在 vb.net 中创建 4 行类,它接受字符串并将类返回到 VBA。这样做的好处是你实际上在 VBA 中获得了带有 json 数据所有列的 intel-sense。编辑你的帖子和提供一个示例 json 字符串。我将发布可在 Access 中使用的工作 vb 代码。
-
我的第一次尝试是 Albert 将 json 结构的完整模型创建为 VB.NET 中的一个类。 json 是一个深层结构,我发现(来自本网站的另一篇文章)我无法以我想要的方式实现子类。因此,例如不能从 VBA 一直使用智能感知来引用“company.invoice(1).ShipAddress.AddressLine(2)”。我将尝试您的解决方案来传递 Dictionary 对象。