将大型Json解析为XML(系统内存异常)

时间:2015-04-13 04:42:42

标签: xml json vb.net

JSON文件(非常大)和脚本如下。

脚本

Dim JsonContents As String = IO.File.ReadAllText(filePath)

Dim doc As XmlDocument = CType(JsonConvert.DeserializeXmlNode("{""root"":" + JsonContents + "}", "root"), XmlDocument)

Dim Document As New XDocument
Dim query As String

Document = XDocument.Parse(doc.OuterXml)

query = "importJSON"

Dim CMD As New SqlCommand(query)
CMD.Parameters.Add("@type", SqlDbType.Int).Value = importType
CMD.Parameters.Add("@xmlObject", SqlDbType.Xml).Value = Document.ToString
......

JSON文件

  

[{“ID”:“001”,“NAME”:“Den.Y”,“GENDER”:“M”,年龄:“18”}]


我想将其转换为XML,然后传递给SQL服务器。但我发现JSON太大了,然后“系统内存异常”被抛出

 Dim doc As XmlDocument =
 CType(JsonConvert.DeserializeXmlNode("{""root"":" + JsonContents +
 "}", "root"), XmlDocument)

我不知道如何修改脚本以便在没有内存的情况下转换大型JSON文件,但小尺寸JSON在此脚本上运行良好。

我在网上搜索,有些人建议使用XMLReader,但我不知道如何处理它。

[解决方案]

感谢大家的努力。最后,我将大型JSON文件剪切下来以防止脚本内存不足。现在它适用于大型JSON。

1 个答案:

答案 0 :(得分:1)

首先,了解一下CLR如何处理内存非常重要。 CLR使用分代垃圾收集器,其中内存被移动到更高阶的生成,因为它在每个集合中存活。此外,还有一个特殊的代,称为“大对象堆”(LOH),用于特定大小和更大的对象。你的JSON字符串几乎肯定会在这里结束。重要的是要知道LOH很少收集;更糟糕的是,它几乎从不压缩。这意味着即使在从内存中收集并删除了一个对象之后,仍然在使用为进程中的对象保留的虚拟地址空间 ...并记住您的进程只有2GB的地址默认为空格。

除此之外,我们可以在代码中看到可能导致问题的内容。 如果(并且“if”将在某一时刻变得很重要)你正在进行直接字符串连接,这段摘录最终会创建三个复制字符串的对象:

"{""root"":" + JsonContents + "}"

"{""root"":" + JsonContents部分有一个副本,尾随+ "}"有第二个新副本。值得庆幸的是,编译器通常会将其重新编写为更高效的内容,并且最终会有一个额外的副本。当我在这里时,让我们不要忘记将这些字节从旧字符串移动到新字符串的所有CPU工作。

一个或两个额外的副本通常不是太大的问题。我通常看到人们在处理使用双倍算法的文档时会遇到麻烦,在这种情况下,它会读取数据流并在每次缓冲区填满时分配一个新旧缓冲区的两倍大的缓冲区。 .Net中的大多数集合类型都以这种方式工作。我不熟悉JSONConvert类型的内部,但DeserializeXmlNode()方法可能以这种方式工作。如果是这种情况,您需要找到另一种方法来创建xml文档。

但是,我们不必看那么远,找到一个反复创建新字符串对象的过程。看看documentation for File.ReadAllText()

  

此方法打开一个文件,读取文件的每一行,然后将每一行添加为字符串元素。

哦,哦。这听起来像是一遍又一遍地连接到一个字符串。如果这确实是导致错误的原因,我希望File.ReadAllText()调用是进程中断的地方。但是,我可能会在开头显示的字符串连接创建的额外副本,以及用于DeserializeXmlNode()调用和CType()强制转换结果的任何内存 - 请记住:如果CType()实际上是做任何工作,那将是两个独立的(大)物体 - 是打破骆驼背部的吸管。

你知道文件有多大,所以我们可以通过从StreamReader读取并将每一行写入使用{{3创建的StringBuilder对象来更好地做 lot }}。这样,所有中间步骤都足够小,以避免大对象堆。但我怀疑这还不够,您需要找到JSONConvert类型的替代方法来创建XmlDocument对象。

当然,您的JSON文件可能就是那么大。在这种情况下,您可能需要将其拆分为多次调用数据库,并一次从文件中读取一个部分。即使在64位系统上,.Net中也不允许单个内存对象超过2GB(进程可以变大,但单个对象不能)。