You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解析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  '&quot;ariregistri_kood&quot;:' $ Rida
      insert into tala (regnr) values ( stre( rida, '&quot;ariregistri_kood&quot;:' ) )

    * "emtak_kood":"73111",
    case  '&quot;emtak_kood&quot;:' $ Rida
      repl emtak_kood with stre( rida, '&quot;emtak_kood&quot;:&quot;', '&quot;' )
      
    * "emtak_tekstina":"Reklaamiagentuurid",
    case  '&quot;emtak_tekstina&quot;:' $ Rida
      repl emtak_tekstina with stre( rida, '&quot;emtak_tekstina&quot;:&quot;', '&quot;' )
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:15:31