如何解析3GB JSON文件并生成指定结构的Cursor
问题描述
现有一个3GB的JSON文件,内容为对象数组结构,示例如下:
[ { "ariregistri_kood":16372442, "nimi":"000 Holdings OÜ", "yldandmed":{ ...(省略部分内容) "teatatud_tegevusalad":[ { "emtak_kood":"73111", "emtak_tekstina":"Reklaamiagentuurid", ... } ] } ]需求是从该文件提取数据,生成包含以下3列的Cursor:
ariregistri_kood I emtak_kood C(5), emtak_tekstina M尝试使用以下VFP代码解析时,
feof()立即返回true,得到空Cursor:create cursor tala ( ariregistri_kood I, emtak_kood V(5), emtak_tekstina M ) fp = fopen( '4gbfile.json' ) if fp<0 messagebox('error') return endif DO WHILE !FEOF(fp) rida = FGETS(fp) do case case '"ariregistri_kood":' $ Rida insert into tala (regnr) values ( stre( rida, '"ariregistri_kood":' ) ) * "emtak_kood":"73111", case '"emtak_kood":' $ Rida repl emtak_kood with stre( rida, '"emtak_kood":"', '"' ) * "emtak_tekstina":"Reklaamiagentuurid", case '"emtak_tekstina":' $ Rida repl emtak_tekstina with stre( rida, '"emtak_tekstina":"', '"' ) endcase enddo尝试使用JSON解析库时出现内存不足错误,询问如何解析文件获取所需3个属性,也可采用XML格式版本的解析方案。
解决方案
一、JSON流式解析(避免内存溢出)
3GB的JSON无法一次性加载到内存,必须逐块处理,同时修正原代码的字段名错误、数据提取逻辑问题,确保关联企业编码和对应的业务类别:
CREATE CURSOR tala (ariregistri_kood I, emtak_kood C(5), emtak_tekstina M) LOCAL fp, rida, currentRegNr, inTegevusalad, currentEmtakKood, currentEmtakTekst fp = FOPEN('4gbfile.json', 0) && 只读模式打开文件 IF fp < 0 MESSAGEBOX('无法打开目标JSON文件') RETURN ENDIF currentRegNr = 0 inTegevusalad = .F. currentEmtakKood = '' currentEmtakTekst = '' DO WHILE !FEOF(fp) rida = FGETS(fp, 8192) && 每次读取8KB,适配长行场景 rida = ALLTRIM(rida) * 提取企业编码ariregistri_kood IF '"ariregistri_kood":' $ rida LOCAL startPos, endPos startPos = AT('"ariregistri_kood":', rida) + LEN('"ariregistri_kood":') endPos = AT(',', rida, startPos) IF endPos = 0 endPos = AT('}', rida, startPos) ENDIF IF endPos > startPos currentRegNr = VAL(SUBSTR(rida, startPos, endPos - startPos)) ENDIF * 进入新企业对象,重置业务类别缓存 currentEmtakKood = '' currentEmtakTekst = '' inTegevusalad = .F. ENDIF * 判断是否进入业务类别数组 IF '"teatatud_tegevusalad":[' $ rida inTegevusalad = .T. ENDIF * 提取业务编码emtak_kood(仅在业务类别数组内生效) IF inTegevusalad AND '"emtak_kood":' $ rida startPos = AT('"emtak_kood":"', rida) + LEN('"emtak_kood":"') endPos = AT('"', rida, startPos) IF endPos > startPos currentEmtakKood = SUBSTR(rida, startPos, endPos - startPos) ENDIF ENDIF * 提取业务名称emtak_tekstina(仅在业务类别数组内生效) IF inTegevusalad AND '"emtak_tekstina":' $ rida startPos = AT('"emtak_tekstina":"', rida) + LEN('"emtak_tekstina":"') endPos = AT('"', rida, startPos) IF endPos > startPos currentEmtakTekst = SUBSTR(rida, startPos, endPos - startPos) ENDIF * 两个字段都获取到后,插入记录 IF currentRegNr > 0 AND !EMPTY(currentEmtakKood) AND !EMPTY(currentEmtakTekst) INSERT INTO tala (ariregistri_kood, emtak_kood, emtak_tekstina) ; VALUES (currentRegNr, currentEmtakKood, currentEmtakTekst) * 重置缓存,准备处理下一个业务类别 currentEmtakKood = '' currentEmtakTekst = '' ENDIF ENDIF * 判断是否退出业务类别数组 IF ']' $ rida AND inTegevusalad inTegevusalad = .F. ENDIF ENDDO FCLOSE(fp) BROWSE
二、XML格式解析方案
如果有XML版本,使用SAX流式解析可以更稳定地处理大文件,避免内存溢出。假设XML结构如下:
<companies> <company> <ariregistri_kood>16372442</ariregistri_kood> <nimi>000 Holdings OÜ</nimi> <yldandmed> <teatatud_tegevusalad> <tegevus> <emtak_kood>73111</emtak_kood> <emtak_tekstina>Reklaamiagentuurid</emtak_tekstina> </tegevus> </teatatud_tegevusalad> </yldandmed> </company> </companies>
对应的VFP代码:
CREATE CURSOR tala (ariregistri_kood I, emtak_kood C(5), emtak_tekstina M) LOCAL oSAX, currentRegNr, currentEmtakKood, currentEmtakTekst, inTegevus, collectingText currentRegNr = 0 currentEmtakKood = '' currentEmtakTekst = '' inTegevus = .F. collectingText = '' * 创建SAX解析器实例 oSAX = CREATEOBJECT("MSXML2.SAX2Reader.6.0") oSAX.contentHandler = CREATEOBJECT("SAXContentHandler") * 自定义SAX内容处理类 DEFINE CLASS SAXContentHandler AS Session PROCEDURE startElement(strNamespaceURI, strLocalName, strQName, oAttributes) DO CASE CASE strLocalName = "ariregistri_kood" THIS.parent.collectingText = "regNr" CASE strLocalName = "tegevus" THIS.parent.inTegevus = .T. THIS.parent.currentEmtakKood = '' THIS.parent.currentEmtakTekst = '' CASE strLocalName = "emtak_kood" AND THIS.parent.inTegevus THIS.parent.collectingText = "emtakKood" CASE strLocalName = "emtak_tekstina" AND THIS.parent.inTegevus THIS.parent.collectingText = "emtakTekst" ENDCASE ENDPROC PROCEDURE characters(strChars) DO CASE CASE THIS.parent.collectingText = "regNr" THIS.parent.currentRegNr = VAL(strChars) CASE THIS.parent.collectingText = "emtakKood" THIS.parent.currentEmtakKood = ALLTRIM(strChars) CASE THIS.parent.collectingText = "emtakTekst" THIS.parent.currentEmtakTekst = ALLTRIM(strChars) ENDCASE ENDPROC PROCEDURE endElement(strNamespaceURI, strLocalName, strQName) DO CASE CASE strLocalName = "tegevus" THIS.parent.inTegevus = .F. * 插入完整记录 IF THIS.parent.currentRegNr > 0 AND !EMPTY(THIS.parent.currentEmtakKood) INSERT INTO tala (ariregistri_kood, emtak_kood, emtak_tekstina) ; VALUES (THIS.parent.currentRegNr, THIS.parent.currentEmtakKood, THIS.parent.currentEmtakTekst) ENDIF CASE INLIST(strLocalName, "ariregistri_kood", "emtak_kood", "emtak_tekstina") THIS.parent.collectingText = "" ENDCASE ENDPROC ENDDEFINE * 启动XML解析 TRY oSAX.parseURL("data.xml") && 替换为你的XML文件路径 CATCH TO oErr MESSAGEBOX("XML解析错误: " + oErr.Message) ENDTRY BROWSE
内容的提问来源于stack exchange,提问作者Andrus
相关产品推荐
相关产品推荐

