wy168 发表于 2022-8-21 12:24:40

几百行代码实现一个JSON解析器

<div id="2f26d41d-7227-46bc-be6c-354c801a3d51" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/a5989c3b270d48b69712b6c99da38e7c~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=eu8ueeYc13h%2FEwa1BOWTXVYelp0%3D" style="width:100%;"></div><h1 id="c5a17ad6-6764-41a9-8793-760107ff38b6" style="font-size:20px;margin:20px 0px;font-weight:700;">前言</h1><p id="1ca8b316-bfa4-4455-9ab1-793825be870f" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="86d5e57c-c8d1-4bab-af88-0ae4eaac23bb" style="font-size:18px;margin:20px 0px;text-align:left;">之前在写<a id="e49eb1bd-9bb2-4600-8a8f-c459d0652a45" style="font-size:18px;margin:20px 0px;text-align:left;">gscript</a>时我就在想有没有利用编译原理实现一个更实际工具?毕竟真写一个语言的难度不低,并且也很难真的应用起来。</span></p><p id="f21dc64f-d607-4953-bdca-220ee8021905" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="a508a33f-5214-42a7-864c-c32761fa05b3" style="font-size:18px;margin:20px 0px;text-align:left;">一次无意间看到有人提起</span><span id="42c0092e-c078-4a10-8f84-62cd745023e0" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>解析器,这类工具充斥着我们的日常开发,运用非常广泛。</p><p id="6e10ec4b-3a91-43ad-811c-0a5ee06c5d9f" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="ec712bfa-db9f-4f39-b744-5693ab2696f6" style="font-size:18px;margin:20px 0px;text-align:left;">以前我也有思考过它是如何实现的,过程中一旦和编译原理扯上关系就不由自主的劝退了;但经过这段时间的实践我发现实现一个</span><span id="9aa5b8ee-9158-4b68-99d4-eeec4ae6ae89" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>解析器似乎也不困难,只是运用到了编译原理前端的部分知识就完全足够了。</p><p id="4f010279-c17d-47c5-80ac-4b6199e29cfc" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="7ee06c27-af39-47b8-9950-2377ffb98105" style="font-size:18px;margin:20px 0px;text-align:left;">得益于</span><span id="a6bce24f-53db-4016-8d8a-8f5a61bcd203" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>的轻量级,同时语法也很简单,所以核心代码大概只用了 800 行便实现了一个语法完善的<span id="ac8ba042-3583-4a9c-aac4-f265f95088e0" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>解析器。</p><p id="22097c5a-f4fe-4da5-9aa6-16c4dd827141" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="01c3695d-c8bd-4c75-a128-5b6807e7f88b" style="font-size:18px;margin:20px 0px;text-align:left;">首先还是来看看效果:</span></p><pre id="ad789b84-3a77-4620-8031-eccf335f340c" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="617da3ee-a394-4d96-9db8-459b7a156192" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="e756074b-0d5b-4bbd-9db4-07934d3b11c0" style="font-size:18px;margin:20px 0px;text-align:left;">import</span><span id="8276d0b8-57fa-4d77-8dd5-44abeb7694d4" style="font-size:18px;margin:20px 0px;text-align:left;">"github.com/crossoverJie/gjson"</span><span id="086c03cb-791a-48bf-bb74-81fbdfd5e40d" style="font-size:18px;margin:20px 0px;text-align:left;">func<span id="a3731cac-3ccc-413c-9fe6-064f6a4cf759" style="font-size:18px;margin:20px 0px;text-align:left;">TestJson</span><span id="9f889bf3-0d5d-42b9-bc5a-d335765e3d8d" style="font-size:18px;margin:20px 0px;text-align:left;">(t *testing.T)</span></span>{ str := `{<span id="ae4836fc-8af1-4fa4-b953-31081143d2cf" style="font-size:18px;margin:20px 0px;text-align:left;">"glossary"</span>: {<span id="ac3e2624-002c-4d59-aa77-54825ed7453f" style="font-size:18px;margin:20px 0px;text-align:left;">"title"</span>:<span id="9568f365-521c-4f18-b23c-21fe9a6fcc9b" style="font-size:18px;margin:20px 0px;text-align:left;">"example glossary"</span>,<span id="4f0a2679-474f-4943-a0b0-9c816698b204" style="font-size:18px;margin:20px 0px;text-align:left;">"age"</span>:<span id="b10d34ac-90bd-4488-8853-93aad0aa4c2b" style="font-size:18px;margin:20px 0px;text-align:left;">1</span>,<span id="4f6bf25d-bdd9-48e2-a30e-5e75bb7ec23d" style="font-size:18px;margin:20px 0px;text-align:left;">"long"</span>:<span id="2549bb6c-ab09-45b8-a9bb-274a339b0350" style="font-size:18px;margin:20px 0px;text-align:left;">99.99</span>,<span id="3baed10b-cdf6-4fbd-98c4-a58b12530363" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossDiv"</span>: {<span id="92def39c-3df5-4563-9adf-a182842efccf" style="font-size:18px;margin:20px 0px;text-align:left;">"title"</span>:<span id="1f048920-a671-401b-b56c-e1cc0f09fa5d" style="font-size:18px;margin:20px 0px;text-align:left;">"S"</span>,<span id="1d32fb1b-ae1b-4e07-a1e0-a774801220e9" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossList"</span>: {<span id="062f1b2a-3c75-41cd-9749-b2a35e3f20a6" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossEntry"</span>: {<span id="ab10bbdc-b327-4043-b0a2-77df660b31e3" style="font-size:18px;margin:20px 0px;text-align:left;">"ID"</span>:<span id="dc0d0a3a-e530-40c7-a175-6c0c2bfb6a0c" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>,<span id="a0f17a7d-8458-4d29-89cf-c92e4f052218" style="font-size:18px;margin:20px 0px;text-align:left;">"SortAs"</span>:<span id="aaad805a-cb4e-452a-b49b-ec65a0fe5a46" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>,<span id="3633f46f-f17e-464b-bc2f-1601b6568393" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossTerm"</span>:<span id="bac430c7-7f0a-4245-a2f4-0b2d0ffe5dcd" style="font-size:18px;margin:20px 0px;text-align:left;">"Standard Generalized Markup Language"</span>,<span id="d913a4c5-14a4-4ee8-915d-be540890c37e" style="font-size:18px;margin:20px 0px;text-align:left;">"Acronym"</span>:<span id="58b7b7ba-c190-4912-a3fe-99bad0cdc311" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>,<span id="a07c689c-7796-473d-9208-c40db8b7f064" style="font-size:18px;margin:20px 0px;text-align:left;">"Abbrev"</span>:<span id="c33505ae-d1e7-4f01-abe3-8f8df661b6a6" style="font-size:18px;margin:20px 0px;text-align:left;">"ISO 8879:1986"</span>,<span id="6efb59e4-397b-42ee-95b0-a7304a71e8a8" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossDef"</span>: {<span id="db149901-c40e-4e8a-9e60-61aaa6698ec8" style="font-size:18px;margin:20px 0px;text-align:left;">"para"</span>:<span id="7fb44a39-e057-44ea-8e4b-755be954ebca" style="font-size:18px;margin:20px 0px;text-align:left;">"A meta-markup language, used to create markup languages such as DocBook."</span>,<span id="88030a02-ebde-4f71-a35a-becbef6927c6" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossSeeAlso"</span>: [<span id="4121f5b5-5663-4f3e-bed6-c33746fb2167" style="font-size:18px;margin:20px 0px;text-align:left;">"GML"</span>,<span id="1e45414f-5126-4a3a-a829-e447be609d6b" style="font-size:18px;margin:20px 0px;text-align:left;">"XML"</span>,<span id="76f293cc-7663-4617-9c16-76262f05ca78" style="font-size:18px;margin:20px 0px;text-align:left;">true</span>,<span id="2dcd1b74-7b16-42c2-8d22-caa477e3b66d" style="font-size:18px;margin:20px 0px;text-align:left;">null</span>]                   },<span id="a426a143-2149-43ed-9b2f-746e79ab5af6" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossSee"</span>:<span id="bff9ad05-907a-420c-bd3a-9ef6193e584c" style="font-size:18px;margin:20px 0px;text-align:left;">"markup"</span>}         }       }   }}` decode, err := gjson.Decode(str)<span id="23ba116d-4f97-4956-9e21-77ef77aaf6f7" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Nil(t, err) fmt.Println(decode) v := decode.(map<span id="c9f3c88d-ae50-453e-b682-08f43d40a91b" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="f513f588-ec0f-4e4d-a367-783d43d1325b" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{}) glossary := v[<span id="dba285d9-2abc-48a7-a9fd-0e087ec59be8" style="font-size:18px;margin:20px 0px;text-align:left;">"glossary"</span>].(map<span id="de7e75ac-b6a2-4375-a717-7825026cb04d" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="44b8078e-7cb8-4125-80c9-c05ddf62f17e" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{})<span id="99ee2d1b-3a51-4c18-aebe-a296473abc08" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossary[<span id="e96ffa9f-69cf-40c1-88b4-3e38c24cdc4b" style="font-size:18px;margin:20px 0px;text-align:left;">"title"</span>],<span id="97ab478b-de02-4673-ae8e-5e71977206ed" style="font-size:18px;margin:20px 0px;text-align:left;">"example glossary"</span>)<span id="544a6d63-9071-4f99-a24d-f5071e32f70a" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossary[<span id="d16b0d02-803e-45ff-aa16-65fca732c64a" style="font-size:18px;margin:20px 0px;text-align:left;">"age"</span>],<span id="05b61300-1ea1-49fd-aa47-0680697df116" style="font-size:18px;margin:20px 0px;text-align:left;">1</span>)<span id="27fb5fa0-debd-47c4-8960-b8551f802b51" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossary[<span id="be95a990-be6a-47e7-a7ee-efa6a02c633c" style="font-size:18px;margin:20px 0px;text-align:left;">"long"</span>],<span id="ba5ad1d5-d553-40f0-9aac-492dd2366ffe" style="font-size:18px;margin:20px 0px;text-align:left;">99.99</span>) glossDiv := glossary[<span id="1c535e9e-9341-4390-abab-de37447327b3" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossDiv"</span>].(map<span id="7e56b5da-45d4-468e-b077-5b271c8aa317" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="ecad305c-c7d6-4f34-812c-b09e373c2f56" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{})<span id="3d4dac87-c9f2-4f8d-952d-3c6960f68a6c" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossDiv[<span id="a4d1feb8-ba80-49a6-bc20-5983ec4b5af3" style="font-size:18px;margin:20px 0px;text-align:left;">"title"</span>],<span id="7abe25d1-9a59-4d13-9a2e-799ead2564e4" style="font-size:18px;margin:20px 0px;text-align:left;">"S"</span>) glossList := glossDiv[<span id="22102f84-d106-4962-ba20-71c43ed89bdb" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossList"</span>].(map<span id="c79b9eac-1fd8-432e-a789-ee05b1dc21f5" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="c34b43f9-68b1-4410-9897-95f6765d31f9" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{}) glossEntry := glossList[<span id="46e56c73-f9e7-4d20-b1ae-688691dea498" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossEntry"</span>].(map<span id="40b48bc1-22a9-4470-b62f-90df1f577b3c" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="3194c9a0-964b-45ab-8daf-df99307f5550" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{})<span id="73f209b0-bd9a-4719-b689-181c8886747c" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="2478a7bc-1a06-446c-aa98-7a1c32b6592d" style="font-size:18px;margin:20px 0px;text-align:left;">"ID"</span>],<span id="9059666e-1286-45cc-9fcd-e8a56e1be84b" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>)<span id="7953009b-0e50-47e2-af05-f78399a11b83" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="ff86b931-9041-4955-9317-9b324d80717c" style="font-size:18px;margin:20px 0px;text-align:left;">"SortAs"</span>],<span id="5112ecf4-c0ca-4206-a329-c98502e5de8f" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>)<span id="5dd9068f-9b77-4618-bcb7-73afc1b0a7c8" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="29639998-21cb-4508-817f-fe99956e794f" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossTerm"</span>],<span id="a80663d7-6ef4-4ed9-95ab-f7ed8a90ce81" style="font-size:18px;margin:20px 0px;text-align:left;">"Standard Generalized Markup Language"</span>)<span id="51da939a-5aa8-4c29-a2b1-9e8658940ebd" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="e166efe9-c0bb-4932-84f2-68816e79442f" style="font-size:18px;margin:20px 0px;text-align:left;">"Acronym"</span>],<span id="192c9e31-0088-4ce1-9333-3626ab630ed1" style="font-size:18px;margin:20px 0px;text-align:left;">"SGML"</span>)<span id="9a8fa036-e030-44bf-9a5e-5b3a16d2665d" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="44bedf0e-9b14-4e13-83ad-980344f9a010" style="font-size:18px;margin:20px 0px;text-align:left;">"Abbrev"</span>],<span id="f8ed7f82-5594-4c59-8dac-340037ccd17c" style="font-size:18px;margin:20px 0px;text-align:left;">"ISO 8879:1986"</span>) glossDef := glossEntry[<span id="079b9b2f-35cb-478e-b6b1-25091a01d5f6" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossDef"</span>].(map<span id="bb505c39-bf97-465c-a438-649f6924a5b8" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="5568293c-ceb7-4e0c-9c8c-560f389d4f6a" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{})<span id="7bf771cb-6a16-4637-ba03-184666054ce0" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossDef[<span id="7dc91a47-7d91-4763-80b4-8f7613a85d95" style="font-size:18px;margin:20px 0px;text-align:left;">"para"</span>],<span id="0f41e06b-8075-4e24-a23e-214bf726c730" style="font-size:18px;margin:20px 0px;text-align:left;">"A meta-markup language, used to create markup languages such as DocBook."</span>) glossSeeAlso := glossDef[<span id="2e9805d4-3ce9-482c-9c9c-4d54966a8477" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossSeeAlso"</span>].(*[]<span id="0d8ab6bd-cffb-4f1b-8cb6-634aa2e4db11" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="cab29024-01a0-4fa7-b546-0d494a463e17" style="font-size:18px;margin:20px 0px;text-align:left;">interface</span></span>{})<span id="f4e36a39-c33f-4686-87ff-415a239e07ca" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, (*glossSeeAlso)[<span id="dd8c6ebf-7521-418f-8a7c-5e58610fe36c" style="font-size:18px;margin:20px 0px;text-align:left;">0</span>],<span id="f9d3f3db-63f4-4c25-8c10-9569911eca1e" style="font-size:18px;margin:20px 0px;text-align:left;">"GML"</span>)<span id="88107088-8237-45cb-9a83-727c9047f316" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, (*glossSeeAlso)[<span id="e23fe421-b378-4215-b5d9-211934319a8d" style="font-size:18px;margin:20px 0px;text-align:left;">1</span>],<span id="f7eaea1a-41ee-4bf6-89dd-d49e98a88680" style="font-size:18px;margin:20px 0px;text-align:left;">"XML"</span>)<span id="47f0cdf2-673f-4121-8ab9-e325a12cf605" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, (*glossSeeAlso)[<span id="e516044d-f391-4ef3-8079-69b0534fb9c5" style="font-size:18px;margin:20px 0px;text-align:left;">2</span>],<span id="02114ccb-4c6e-47ae-ad65-d8aac3added7" style="font-size:18px;margin:20px 0px;text-align:left;">true</span>)<span id="bd6c1cf7-3bb9-4808-befc-07fa9c5d24a9" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, (*glossSeeAlso)[<span id="25667db4-a70f-4c17-914e-a1ac3e1c6464" style="font-size:18px;margin:20px 0px;text-align:left;">3</span>],<span id="62cc39e7-af8b-4ffc-95ed-52008073e75b" style="font-size:18px;margin:20px 0px;text-align:left;">""</span>)<span id="73253a2b-e4e5-481b-9f23-b67604a6c2dd" style="font-size:18px;margin:20px 0px;text-align:left;">assert</span>.Equal(t, glossEntry[<span id="4119c8db-9ca7-4e2f-af8a-47f7e66017d3" style="font-size:18px;margin:20px 0px;text-align:left;">"GlossSee"</span>],<span id="d2dcf71e-4d0c-4717-a0e1-ece8e2480326" style="font-size:18px;margin:20px 0px;text-align:left;">"markup"</span>)}</code></pre><p id="457f061f-5a72-48a3-8f7a-e13172696099" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="ca40ca39-52f4-4dbe-8864-c68e1507af5b" style="font-size:18px;margin:20px 0px;text-align:left;">从这个用例中可以看到支持字符串、布尔值、浮点、整形、数组以及各种嵌套关系。</span></p><h1 id="58ffbda6-31e4-457c-8f8a-335587d4b80b" style="font-size:20px;margin:20px 0px;font-weight:700;">实现原理</h1><div id="2709c216-7ba9-4280-84d3-da5bf590f21a" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/4b9d02fc58574010b10a98fcb216164f~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=29mgFLTqjGEogfE8JCIjXPTTjbE%3D" style="width:100%;"></div><p id="33a74aa1-b654-4f08-a371-15b71e745dca" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="e8583a57-bafd-45ff-afb0-8df1db166718" style="font-size:18px;margin:20px 0px;text-align:left;">这里简要说明一下实现原理,本质上就是两步:</span></p><ol id="7d7b7aa0-5697-4d0f-a2b9-3cd922e5024d" style="font-size:18px;margin:20px 0px;text-align:left;"><li id="fc782d8f-cc96-488f-bb43-6899fc086a80" style="font-size:18px;margin:20px 0px;text-align:left;"><strong id="d927cdc0-7cec-45f7-8fb5-668ec5a2c4fc" style="font-size:18px;margin:20px 0px;font-weight:700;"><span id="77ad5a4c-26f6-4e82-8eb6-1de8f83a9a27" style="font-size:18px;margin:20px 0px;text-align:left;">词法解析</span></strong>:根据原始输入的<span id="d2dfcb3c-9700-438e-aedb-ae2b6e356e8b" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>字符串解析出 token,也就是类似于<span id="62636c90-6a97-4f5c-b83b-5e05cad16cac" style="font-size:18px;margin:20px 0px;text-align:left;">"{" "obj" "age" "1" "[" "]"</span>这样的标识符,只是要给这类标识符分类。</li><li id="e52bb7c6-9fb4-408d-8cfd-e18133e4f9fd" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="db6ac840-1a06-43c5-a8b5-23e4c79873dc" style="font-size:18px;margin:20px 0px;text-align:left;">根据生成的一组</span><span id="2315b940-c28e-4102-b145-1f68d81f94f5" style="font-size:18px;margin:20px 0px;text-align:left;">token</span>集合,以流的方式进行读取,最终可以生成图中的树状结构,也就是一个<span id="4c01f5d8-c969-4d30-b784-7b620f3e2488" style="font-size:18px;margin:20px 0px;text-align:left;">JSONObject</span>。</li></ol><p id="c8cd36ca-aec1-43e7-855e-2befd664a6da" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="2ffeb3dc-1f96-4f5b-930e-162bc540f727" style="font-size:18px;margin:20px 0px;text-align:left;">下面来重点看看这两个步骤具体做了哪些事情。</span></p><h1 id="4a3118b6-efe3-428f-bd45-96bf1f3b3f4e" style="font-size:20px;margin:20px 0px;font-weight:700;">词法分析</h1><pre id="256b5d6b-69d1-4f24-b55d-4dade1c3846b" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="899cb0fe-854a-45f9-8bbd-0c6b25e2282a" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject{<span id="cf4e6b59-1210-49ee-9472-e5b060162c50" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="b4acedfe-27b7-4acd-b7bb-6c95b9c23b65" style="font-size:18px;margin:20px 0px;text-align:left;">"name"</span>SepColon:<span id="baf860ea-f301-4fa7-9400-d94f2860c686" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="634f2312-c180-4631-a7d0-c1ed31a5b024" style="font-size:18px;margin:20px 0px;text-align:left;">"cj"</span>SepComma,<span id="ac09ba6b-da84-4a48-8d64-8c507e02de69" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="3189311f-dd9c-4232-86a7-cdba867627dc" style="font-size:18px;margin:20px 0px;text-align:left;">"object"</span>SepColon:BeginObject{<span id="402e9a5f-6e32-455e-b425-456e4c3a7617" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="9ceaa107-618d-4a82-8ca9-edad730bbe9e" style="font-size:18px;margin:20px 0px;text-align:left;">"age"</span>SepColon:<span id="d263028c-cd2e-44e6-bff8-5c89d3a4ef94" style="font-size:18px;margin:20px 0px;text-align:left;">Number</span><span id="56e81d71-8228-47d8-869f-07e53df3f8bb" style="font-size:18px;margin:20px 0px;text-align:left;">10</span>SepComma,<span id="ae8bfadc-92eb-48a6-a199-aecbc23c9509" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="150a4e0f-905d-4abd-a34d-557cb53875ee" style="font-size:18px;margin:20px 0px;text-align:left;">"sex"</span>SepColon:<span id="b08c24f3-8897-4762-872c-c675be5f9f83" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="f403f234-9bf6-4fe3-8ac0-957dfb9d1f57" style="font-size:18px;margin:20px 0px;text-align:left;">"girl"</span>EndObject}SepComma,<span id="24aee7ab-44fd-4317-9c3d-88ee861bb781" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="069fceed-540a-4bc8-bcef-628328eb68f9" style="font-size:18px;margin:20px 0px;text-align:left;">"list"</span>SepColon:BeginArray[</code></pre><p id="7949e5e9-3e43-424f-9f00-0812b9077759" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="577d9f6a-7ab9-47aa-8e0d-b49ec57158f1" style="font-size:18px;margin:20px 0px;text-align:left;">其实词法解析就是构建一个有限自动机的过程(</span><span id="dcd10e1b-3890-4c15-9051-fd45f4e330cd" style="font-size:18px;margin:20px 0px;text-align:left;">DFA</span>),目的是可以生成这样的集合(token),只是我们需要将这些 token进行分类以便后续做语法分析的时候进行处理。</p><p id="49e2f489-f2bd-4710-bee5-9de9b055626d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="6b22a682-6f63-4717-8c18-d6e8b0e7e82d" style="font-size:18px;margin:20px 0px;text-align:left;">比如</span><span id="255c7dc3-4c6a-4ad9-9008-1433a7d758af" style="font-size:18px;margin:20px 0px;text-align:left;">"{"</span>这样的左花括号就是一个<span id="84308fa8-0196-49f8-9cec-50476caef641" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject</span>代表一个对象声明的开始,而<span id="ef754de3-4101-4abb-8974-ced5b7013024" style="font-size:18px;margin:20px 0px;text-align:left;">"}"</span>则是<span id="8563ad07-3526-40db-a605-a66e996e1617" style="font-size:18px;margin:20px 0px;text-align:left;">EndObject</span>代表一个对象的结束。</p><p id="b2130c77-10f9-4ef5-a7af-2a2f9bcf7202" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="9e63552d-2c52-4309-989e-978bdc908307" style="font-size:18px;margin:20px 0px;text-align:left;">其中</span><span id="c4b4b1fc-c98a-4c7c-9d26-aafa33d37977" style="font-size:18px;margin:20px 0px;text-align:left;">"name"</span>这样的就被认为是<span id="e5352a0d-e990-4242-9847-c8db791957a9" style="font-size:18px;margin:20px 0px;text-align:left;">String</span>字符串,以此类推<span id="52ac23fa-e206-4765-bd59-4886b9f90008" style="font-size:18px;margin:20px 0px;text-align:left;">"["</span>代表<span id="0fa58ab6-722f-4ba4-a892-3b009fa06d3c" style="font-size:18px;margin:20px 0px;text-align:left;">BeginArray</span></p><p id="d405b855-ef9d-4e3a-b388-2fb6b82e7226" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="9f67241d-a913-4fd8-a996-a64427528c19" style="font-size:18px;margin:20px 0px;text-align:left;">这里我一共定义以下几种 token 类型:</span></p><pre id="32a86ca4-0036-46a3-8019-c87c57ffb1f8" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="57a6743d-bcc7-48dd-8222-a9629335a89a" style="font-size:18px;margin:20px 0px;text-align:left;">type Token stringconst ( Init      Token =<span id="c684cc34-e816-4e94-a917-e6ec4f9c1d58" style="font-size:18px;margin:20px 0px;text-align:left;">"Init"</span>BeginObject       =<span id="8baff87a-9209-4fec-9226-94e89190ba0b" style="font-size:18px;margin:20px 0px;text-align:left;">"BeginObject"</span>EndObject         =<span id="3072de05-faf4-46bd-9c52-78196a453bc7" style="font-size:18px;margin:20px 0px;text-align:left;">"EndObject"</span>BeginArray      =<span id="9f4d2aae-c378-479b-9f22-b784d846b608" style="font-size:18px;margin:20px 0px;text-align:left;">"BeginArray"</span>EndArray          =<span id="b6baf4f5-2926-40a1-b074-335e7fc078f9" style="font-size:18px;margin:20px 0px;text-align:left;">"EndArray"</span>Null            =<span id="cad54011-0652-4938-a583-1dd32f20d918" style="font-size:18px;margin:20px 0px;text-align:left;">"Null"</span>Null1             =<span id="64b17f5b-010a-4c37-bafd-be2ce96e6033" style="font-size:18px;margin:20px 0px;text-align:left;">"Null1"</span>Null2             =<span id="cd78e746-598a-414f-9583-b34161126a4e" style="font-size:18px;margin:20px 0px;text-align:left;">"Null2"</span>Null3             =<span id="01073094-fe41-45b0-ac65-6a02c8658c49" style="font-size:18px;margin:20px 0px;text-align:left;">"Null3"</span>Number            =<span id="c64f6727-bc29-4b38-bcce-35d530851b78" style="font-size:18px;margin:20px 0px;text-align:left;">"Number"</span>Float             =<span id="b6baf8d0-3f4a-4699-8f79-3ca69a2ba6f3" style="font-size:18px;margin:20px 0px;text-align:left;">"Float"</span>BeginString       =<span id="8c75057b-ec4c-4410-8fe4-79335524403f" style="font-size:18px;margin:20px 0px;text-align:left;">"BeginString"</span>EndString         =<span id="7e81f282-110e-4163-b1a5-9e0e216426ed" style="font-size:18px;margin:20px 0px;text-align:left;">"EndString"</span>String            =<span id="52fd1589-d3c0-4206-bb98-5b092fe44285" style="font-size:18px;margin:20px 0px;text-align:left;">"String"</span><span id="d766b7c2-cae3-4189-8ae4-5179385d4496" style="font-size:18px;margin:20px 0px;text-align:left;">True</span>=<span id="82720b28-0fa6-4d86-899b-5e3eb964d818" style="font-size:18px;margin:20px 0px;text-align:left;">"True"</span>True1             =<span id="47dd11b0-a971-4106-ac4d-a1dc26feb274" style="font-size:18px;margin:20px 0px;text-align:left;">"True1"</span>True2             =<span id="085cb193-f1c7-434b-b052-f1b42cd9be20" style="font-size:18px;margin:20px 0px;text-align:left;">"True2"</span>True3             =<span id="84b5b6d6-f499-49b7-946c-0ed4d9195150" style="font-size:18px;margin:20px 0px;text-align:left;">"True3"</span><span id="e53fa2a3-4712-4397-a416-4f1c902f4f2e" style="font-size:18px;margin:20px 0px;text-align:left;">False</span>=<span id="16432197-f4c4-43aa-8db1-bc694e1d8820" style="font-size:18px;margin:20px 0px;text-align:left;">"False"</span>False1            =<span id="e8aee0b9-8845-4b50-adf9-fa21cf3141f6" style="font-size:18px;margin:20px 0px;text-align:left;">"False1"</span>False2            =<span id="8c7e62b0-9735-499c-b47f-e237aac4f1cb" style="font-size:18px;margin:20px 0px;text-align:left;">"False2"</span>False3            =<span id="559cd8eb-6113-45af-bf1a-a1ce39f4c553" style="font-size:18px;margin:20px 0px;text-align:left;">"False3"</span>False4            =<span id="6c7d5404-9ec3-4564-8a52-a624ffc1879e" style="font-size:18px;margin:20px 0px;text-align:left;">"False4"</span>// SepColon : SepColon =<span id="778a6349-85a2-4f76-9da6-ef80f1fc7226" style="font-size:18px;margin:20px 0px;text-align:left;">"SepColon"</span>// SepComma , SepComma =<span id="4597cfe3-e7cb-4992-a497-273d3b07ae96" style="font-size:18px;margin:20px 0px;text-align:left;">"SepComma"</span>EndJson=<span id="53a1154d-2e4d-49b3-b137-36ee4c835441" style="font-size:18px;margin:20px 0px;text-align:left;">"EndJson"</span>)</code></pre><blockquote id="f3047dc1-4292-488a-b166-d578deca9964" style="font-size:18px;margin:20px 0px;text-align:left;"><p id="393e0ec6-25d0-419b-a55f-7b1f6e99fd65" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="205be6ba-15d9-4249-a035-f9396eb77547" style="font-size:18px;margin:20px 0px;text-align:left;">其中可以看到 true/false/null 会有多个类型,这点先忽略,后续会解释。</span></p></blockquote><p id="e0c2acab-6ceb-4201-86cc-6cb356c9965d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="45100cf1-b2b5-4bfc-9306-82df685c139d" style="font-size:18px;margin:20px 0px;text-align:left;">以这段</span><span id="6eccb215-fec2-4d71-b8d7-2e91032774f0" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>为例:<span id="06430a24-bc1c-461f-8e1b-c52224335581" style="font-size:18px;margin:20px 0px;text-align:left;">{"age":1}</span>,它的状态扭转如下图:</p><div id="fc80e23d-2a57-4ffc-ab5f-786c6db82dc7" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/c6c74eb39af14b0baa7aa38dc7724197~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=tF%2Fsd479WJw5ibNnDqFlrF0LSG4%3D" style="width:100%;"></div><p id="9d35dd01-a407-4aa2-b53b-4a7f930c2588" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="7136bdd3-dcd0-495c-b91d-5980a13ae829" style="font-size:18px;margin:20px 0px;text-align:left;">总的来说就是依次遍历字符串,然后更新一个全局状态,根据该状态的值进行不同的操作。</span></p><p id="03c0f4c6-05d7-4838-b5c1-e3b73970ae50" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="2b31d73e-f87a-48ef-8517-be0690bef3ab" style="font-size:18px;margin:20px 0px;text-align:left;">部分代码如下:</span></p><div id="6aced4d4-add9-471c-9f51-cb117743d5a3" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/19db9824ac6e4f2a9370c48edcb1a464~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=xUYM3QwN4XDvFRf0u5e1InrWYnc%3D" style="width:100%;"></div><div id="b061af10-49cb-49e2-a4f0-bbde26b282cc" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/dfb2327da9ff4bfe935144f6acb317f2~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=kNfDR%2BSAU5Ndh9wHOyjdryaBsJc%3D" style="width:100%;"></div><p id="7ee5c7fd-56e6-4863-940e-bd1ee223ebc5" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="db89b055-9e13-4202-aeb8-49b08dd1baea" style="font-size:18px;margin:20px 0px;text-align:left;">感兴趣的朋友可以跑跑单例 debug 一下就很容易理解:</span></p><p id="a8668f97-e053-4a5d-8e39-046dfc7f9dc5" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="94355ac4-09a7-4221-a631-c33c28c6363f" style="font-size:18px;margin:20px 0px;text-align:left;">https://github.com/crossoverJie/gjson/blob/main/token_test.go</span></p><p id="8f36df12-fac2-4996-abb4-132ae87ce2b9" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="4b8ed4d8-4a20-4c9a-ae07-85f62a4c6024" style="font-size:18px;margin:20px 0px;text-align:left;">以这段 JSON 为例:</span></p><pre id="b99c95b1-fe42-43d8-9d0b-a10a0a6bc0f7" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="9c4cdf79-dc7e-4813-a85b-b05086867247" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="acc58273-ff30-4ebb-8eea-a187cf133cb7" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="27bfe836-32c5-4acc-84f0-039cd45d3e15" style="font-size:18px;margin:20px 0px;text-align:left;">func</span><span id="d7240f4f-07ed-4259-b001-9068a539f576" style="font-size:18px;margin:20px 0px;text-align:left;">TestInitStatus</span><span id="7e49c954-da5f-460d-8509-a7fc8e9fed52" style="font-size:18px;margin:20px 0px;text-align:left;">(t *testing.T)</span></span>{ str :=<span id="9f66f255-af31-4010-974f-e268b63c77af" style="font-size:18px;margin:20px 0px;text-align:left;">`{"name":"cj", "age":10}`</span>tokenize, err := Tokenize(str) assert.Nil(t, err)<span id="23d90f03-17ab-4d63-8d6a-7b18013a0110" style="font-size:18px;margin:20px 0px;text-align:left;">for</span>_, tokenType :=<span id="ead11233-7748-411e-aa4d-494c1880b2c7" style="font-size:18px;margin:20px 0px;text-align:left;">range</span>tokenize {fmt.Printf(<span id="ee5224d3-bfc7-4e95-aa4c-2bfdcf66dd4a" style="font-size:18px;margin:20px 0px;text-align:left;">"%s%s\n"</span>, tokenType.T, tokenType.Value) }}</code></pre><p id="45df409c-b9a7-4bc5-891f-f92799e65a01" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="88d815f6-2986-4038-bf16-de7782ff50ae" style="font-size:18px;margin:20px 0px;text-align:left;">最终生成的</span><span id="5fca5b56-630c-47d2-b45b-c31f8381bf15" style="font-size:18px;margin:20px 0px;text-align:left;">token</span>集合如下:</p><pre id="37c2600c-4532-4091-a7cd-287387d7136b" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="3fb910ea-8bdf-40cb-b310-69378a732eaa" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject{<span id="0374d168-2130-4f35-8437-7ec7d7d75238" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="451f226b-8dc6-45dc-9221-acac120c302b" style="font-size:18px;margin:20px 0px;text-align:left;">"name"</span>SepColon:<span id="2aa34193-da6a-437e-b8ec-bb3bf1cce8d1" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="575a4cf2-6261-4741-b5c2-35a33ba82770" style="font-size:18px;margin:20px 0px;text-align:left;">"cj"</span>SepComma,<span id="c5c0ffb9-478d-4646-9146-91c0bd48325f" style="font-size:18px;margin:20px 0px;text-align:left;">String</span><span id="98a145fa-f4eb-4745-82a0-fe5bb6154602" style="font-size:18px;margin:20px 0px;text-align:left;">"age"</span>SepColon:<span id="87e0e3e7-e306-4c30-a628-534bb2f19ee0" style="font-size:18px;margin:20px 0px;text-align:left;">Number</span><span id="a5649889-60b1-4f7a-bbba-6a6ca2f6552c" style="font-size:18px;margin:20px 0px;text-align:left;">10</span>EndObject}</code></pre><h1 id="75ccd625-6fbc-43e4-a04f-951a74db23df" style="font-size:20px;margin:20px 0px;font-weight:700;">提前检查</h1><p id="cf6f1a0f-97cf-4b40-9d77-d271f53ba825" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="43b791c8-66c7-4335-9e94-f67be83e6aa1" style="font-size:18px;margin:20px 0px;text-align:left;">由于</span><span id="0bd69428-3d24-44d6-92f9-2d75f16f9066" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>的语法简单,一些规则甚至在词法规则中就能校验。</p><p id="f685b7fd-4b52-4248-a84e-5f942ea585c5" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="0966fbcd-e114-44fa-a2d1-7305de583d44" style="font-size:18px;margin:20px 0px;text-align:left;">举个例子:</span><span id="11abbb12-e9a7-4e0d-897b-ad22dd54fd01" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>中允许<span id="16c30a3b-097b-4558-97b9-9a402fe7aa3d" style="font-size:18px;margin:20px 0px;text-align:left;">null</span>值,当我们字符串中存在<span id="b4f8e6e5-486d-477b-b8fb-a6b2eb34c42d" style="font-size:18px;margin:20px 0px;text-align:left;">nu nul</span>这类不匹配<span id="9488041b-7f5c-4055-bd15-1a3d8775bb3f" style="font-size:18px;margin:20px 0px;text-align:left;">null</span>的值时,就可以提前抛出异常。</p><div id="09252e00-af76-429d-b57a-ecc8e940f0a6" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/4aaaa8fe3b354895942a2fd05721fcb6~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=FyJJtlKwgDlRUojjw8zZKiwVG%2F0%3D" style="width:100%;"></div><p id="f88db0b6-2ccb-4e83-8806-f246ec505105" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="15a7a359-9bf0-4fa2-b40a-0755b8f79db5" style="font-size:18px;margin:20px 0px;text-align:left;">比如当检测到第一个字符串为 n 时,那后续的必须为</span><span id="fbe1a6ce-a347-478c-956f-61f9a4744dff" style="font-size:18px;margin:20px 0px;text-align:left;">u-&gt;l-&gt;l</span>不然就抛出异常。</p><p id="726d68a1-4d15-4dbd-89d6-0cb7236354fc" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="db7f2d8a-aac6-4c97-add9-083dbda9a96e" style="font-size:18px;margin:20px 0px;text-align:left;">浮点数同理,当一个数值中存在多个 . 点时,依然需要抛出异常。</span></p><div id="e4efaa76-006d-4d64-8476-6dbc8f68dd6c" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/6f59c9e03faa49649127e6bab95b464a~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=zeUaHX0hzsIfWYy1ZZ2zOiz%2F4DM%3D" style="width:100%;"></div><p id="67a0b69c-b1a5-4749-a4b4-8f4be1802374" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="6677d6f9-a88a-4747-aefb-ceda464666ac" style="font-size:18px;margin:20px 0px;text-align:left;">这也是前文提到</span><span id="5b105cda-2369-46ad-b710-6d9ab4a4c903" style="font-size:18px;margin:20px 0px;text-align:left;">true/false/null</span>这些类型需要有多个中间状态的原因。</p><h1 id="aaa48b7e-89ec-4599-acb1-9bd0dd200d2a" style="font-size:20px;margin:20px 0px;font-weight:700;">生成 JSONObject 树</h1><p id="d35fed23-eed7-4e40-a45d-ac1e4b50b237" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="d2a1a023-40f6-4c58-b2ad-c2a1de83719b" style="font-size:18px;margin:20px 0px;text-align:left;">在讨论生成</span><span id="ee13163f-5c3c-4816-83ef-a881f3ac146c" style="font-size:18px;margin:20px 0px;text-align:left;">JSONObject</span>树之前我们先来看这么一个问题,给定一个括号集合,判断是否合法。</p><ul id="56398c70-a747-4eaa-a02a-286eb98db02f" style="font-size:18px;margin:20px 0px;text-align:left;"><li id="d81a8fac-d4ef-4a3c-8d4a-b2c2c464362f" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="1113b87a-94ad-441f-bef2-d414e07df4db" style="font-size:18px;margin:20px 0px;text-align:left;">[&lt;()&gt;]</span>这样是合法的。</li><li id="7d378b2d-9f4e-4d06-9a78-9cc697ef22a2" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="11c1a55c-5418-45c6-812f-baca7d50e4f2" style="font-size:18px;margin:20px 0px;text-align:left;">[&lt;()&gt;)</span>而这样是不合法的。</li></ul><p id="27c6ef04-e58f-4724-b6f8-446126b608f1" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="05377045-86b1-475b-8dca-26c2c16cec80" style="font-size:18px;margin:20px 0px;text-align:left;">如何实现呢?其实也很简单,只需要利用栈就能完成,如下图所示:</span></p><div id="e8f51404-64d3-42a7-8cc7-d6b71742abdf" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/44d75efd8ec34648a0e7091c52871917~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=ULpos9Mm7c6pWYEAitFD%2FsQSeaQ%3D" style="width:100%;"></div><p id="d9911a2e-be2e-453d-b342-ef2a70072fa2" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="b0ca9916-e3cc-4b28-b506-6fba1d229fe9" style="font-size:18px;margin:20px 0px;text-align:left;">利用栈的特性,依次遍历数据,遇到是左边的符号就入栈,当遇到是右符号时就与栈顶数据匹配,能匹配上就出栈。</span></p><p id="895099f5-9fac-4039-b80f-44d61c641a07" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="aa264f95-b9f2-4488-9321-271fc57b90fc" style="font-size:18px;margin:20px 0px;text-align:left;">当匹配不上时则说明格式错误,数据遍历完毕后如果栈为空时说明数据合法。</span></p><p id="6c1338b1-f343-41e5-8246-c1a25c7bcae4" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="a2a70ba8-992a-4c68-9568-0638853e5592" style="font-size:18px;margin:20px 0px;text-align:left;">其实仔细观察</span><span id="0440c8ff-824a-4cdd-a166-0d2ead381987" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>的语法也是类似的:</p><pre id="2ac4fff4-ce48-4756-a32b-94f8b8387c50" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="4a8624c8-26cf-4bb1-8252-cf80d88f5c6e" style="font-size:18px;margin:20px 0px;text-align:left;">{<span id="8872766c-5aef-47b7-9d81-accfcc2970f6" style="font-size:18px;margin:20px 0px;text-align:left;">"name"</span>:<span id="aa761b59-ce0b-42a4-8111-919fb42aee95" style="font-size:18px;margin:20px 0px;text-align:left;">"cj"</span>,<span id="27cf3512-a160-4c49-b372-8daf5fbc1dbc" style="font-size:18px;margin:20px 0px;text-align:left;">"object"</span>: {<span id="b3307c52-21a9-4913-b4e6-84b097e03d33" style="font-size:18px;margin:20px 0px;text-align:left;">"age"</span>:<span id="7621a191-cbc9-4334-9803-821d71c86f9c" style="font-size:18px;margin:20px 0px;text-align:left;">10</span>,<span id="066bd2a1-797c-4064-957c-d6ae432593e8" style="font-size:18px;margin:20px 0px;text-align:left;">"sex"</span>:<span id="80659a19-34ef-47bd-97d1-dd8735953cfa" style="font-size:18px;margin:20px 0px;text-align:left;">"girl"</span>},<span id="e5f93c17-9e06-45ec-ad2e-0d250f8816c1" style="font-size:18px;margin:20px 0px;text-align:left;">"list"</span>: [      {<span id="94ca3906-f13b-4c2d-a60e-d129d5c7b6b2" style="font-size:18px;margin:20px 0px;text-align:left;">"1"</span>:<span id="39ea700a-da25-4e85-926c-e7df32154ac4" style="font-size:18px;margin:20px 0px;text-align:left;">"a"</span>},      {<span id="20eea8dd-1127-4d23-ac85-509707ba304f" style="font-size:18px;margin:20px 0px;text-align:left;">"2"</span>:<span id="e0b999a2-6a9d-4371-8f3e-14fc966ee960" style="font-size:18px;margin:20px 0px;text-align:left;">"b"</span>}    ]}</code></pre><p id="5d47a551-ff06-4e36-8588-45f392a78046" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="c69e13f4-f759-43c8-82ed-8a3fa1c2dc73" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject:{</span>与<span id="fc8b6a13-fdb6-4ab5-b220-faeda3a56793" style="font-size:18px;margin:20px 0px;text-align:left;">EndObject:}</span>一定是成对出现的,中间如论怎么嵌套也是成对的。 而对于<span id="90c30af8-9830-4de3-a862-71eab1e1e286" style="font-size:18px;margin:20px 0px;text-align:left;">"age":10</span>这样的数据,: 冒号后也得有数据进行匹配,不然就是非法格式。</p><p id="c5c84be4-a5a6-4824-894a-a34878b7d215" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="8a13e275-740e-4d89-9f59-8df8c7e7a4a2" style="font-size:18px;margin:20px 0px;text-align:left;">所以基于刚才的括号匹配原理,我们也能用类似的方法来解析</span><span id="86d43d2e-049d-45ac-b1b7-91838fe57c93" style="font-size:18px;margin:20px 0px;text-align:left;">token</span>集合。</p><p id="0167cd2d-a847-4556-88ec-1b39573186f4" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="efb56731-bbf6-4cd4-8a88-adfd68b78c4a" style="font-size:18px;margin:20px 0px;text-align:left;">我们也需要创建一个栈,当遇到</span><span id="510b253a-8909-4a59-81ba-7b6bc28fed90" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject</span>时就入栈一个 Map,当遇到一个<span id="3dc71cf8-de42-4791-90b2-a829fd2b21cf" style="font-size:18px;margin:20px 0px;text-align:left;">String</span>键时也将该值入栈。</p><p id="84a399f4-9fbe-45c5-a173-99bbaf5d5353" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="3cc01c64-cd30-4de7-aed5-a3bb67bc1b87" style="font-size:18px;margin:20px 0px;text-align:left;">当遇到</span><span id="1cd82954-5851-4c5b-a372-48e34b3d01db" style="font-size:18px;margin:20px 0px;text-align:left;">value</span>时,就将出栈一个<span id="ba19bb9e-86bd-43d7-ae97-4737d8620403" style="font-size:18px;margin:20px 0px;text-align:left;">key</span>,同时将数据写入当前栈顶的<span id="40dbbd7e-2180-4d6d-8e7c-121ff68b7fd6" style="font-size:18px;margin:20px 0px;text-align:left;">map</span>中。</p><p id="de539be4-7573-4d1e-af36-f7979fd29b9d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="bd8b9ab9-0ef2-4e66-9388-d22ab0d7fd43" style="font-size:18px;margin:20px 0px;text-align:left;">当然在遍历</span><span id="5563cf30-fe1f-4ae8-9a92-4734ca328de2" style="font-size:18px;margin:20px 0px;text-align:left;">token</span>的过程中也需要一个全局状态,所以这里也是一个<strong id="eff05169-ab7b-4061-8899-340ec4e885ab" style="font-size:18px;margin:20px 0px;font-weight:700;"><span id="730e72c5-34f8-4092-9acd-5c0426a2e3e9" style="font-size:18px;margin:20px 0px;text-align:left;">有限状态机</span></strong>。</p><hr id="0c06d450-c3a6-4592-9c33-6f6aeab80828" style="font-size:18px;margin:20px 0px;text-align:left;"><p id="922ed3f8-ebb2-477d-a0ff-fc1cd8f61d61" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="9d328c06-c458-4a8a-bfcd-a0b5f52c5213" style="font-size:18px;margin:20px 0px;text-align:left;">举个例子:当我们遍历到</span><span id="f43df8da-d261-4138-98e3-23de71e6e212" style="font-size:18px;margin:20px 0px;text-align:left;">Token</span>类型为<span id="43f181c3-05ec-48e9-a2c4-3d6b822162e1" style="font-size:18px;margin:20px 0px;text-align:left;">String</span>,值为<span id="8f42ddcf-a094-452e-8e05-5bf91467e276" style="font-size:18px;margin:20px 0px;text-align:left;">"name"</span>时,预期下一个<span id="10683b90-721b-464f-b884-f3f8565ff36b" style="font-size:18px;margin:20px 0px;text-align:left;">token</span>应当是 :冒号;</p><p id="2525d5e5-4fa4-4b8e-ad6f-30473d2a5f47" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="2bbb3915-3a7e-467e-b48e-37eb22516bfb" style="font-size:18px;margin:20px 0px;text-align:left;">所以我们得将当前的 status 记录为</span><span id="d37499f7-1ea6-4592-b069-7bc0873a2d4f" style="font-size:18px;margin:20px 0px;text-align:left;">StatusColon</span>,一旦后续解析到 token 为<span id="642cb7b5-40be-4db4-a205-91f8c8080285" style="font-size:18px;margin:20px 0px;text-align:left;">SepColon</span>时,就需要判断当前的 status 是否为<span id="fe1041f6-2ded-4da6-81f9-23901c95aacd" style="font-size:18px;margin:20px 0px;text-align:left;">StatusColon</span>,如果不是则说明语法错误,就可以抛出异常。</p><div id="9f855be6-8c22-4744-bbbb-4882e5ecc3c4" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/16972b74101c4e469a5b9e8bca0afdb8~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=VHlYU70w3ouEoMqxhxqudRNt9LU%3D" style="width:100%;"></div><div id="b55534da-92b4-4852-8dcd-96a7ba1eef76" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/650768139e5f45a396c830fe3c9b2765~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=KA57NAhamGLhkbmWWhKrTiLzgoo%3D" style="width:100%;"></div><p id="99070acf-bd74-490d-bc0e-a5eb0c70f973" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="961b221e-93f9-4599-b4ee-305dcda9f38e" style="font-size:18px;margin:20px 0px;text-align:left;">同时值得注意的是这里的</span><span id="94db116d-7c11-4d50-88ee-01251188b918" style="font-size:18px;margin:20px 0px;text-align:left;">status</span>其实是一个<span id="2815570d-9eee-4224-baa1-325d94236c89" style="font-size:18px;margin:20px 0px;text-align:left;">集合</span>,因为下一个状态可能是多种情况。</p><p id="44a1da12-e9fb-4d45-8ea7-51e809e831a6" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="5e58fe54-fbe9-4bbf-bc24-1410a4d96ecb" style="font-size:18px;margin:20px 0px;text-align:left;">{"e":,{"d":{"f":"f"}}]}</span>比如当我们解析到一个<span id="79bf8924-d655-4c0f-854a-02874a37ed6f" style="font-size:18px;margin:20px 0px;text-align:left;">SepColon</span>冒号时,后续的状态可能是<span id="acab1e05-cf8c-4f14-aa51-d6be398063a7" style="font-size:18px;margin:20px 0px;text-align:left;">value</span>或<span id="af4f4815-15bf-4f90-9fdc-2186276d0fcb" style="font-size:18px;margin:20px 0px;text-align:left;">BeginObject {</span>或<span id="23d794fb-3675-4a4d-b004-97421dea2470" style="font-size:18px;margin:20px 0px;text-align:left;">BeginArray [</span></p><div id="9bb254c0-9cae-487c-909e-35e95694b9b5" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/51aa59996a5447959163e93ac20818a6~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=%2FWRKGfKRQ9zhIUY6MKFw4FceVAk%3D" style="width:100%;"></div><p id="974dad63-8f7b-4dca-8f6f-c11dc81534cf" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="fe02db23-5970-464d-8755-a07979afe7a0" style="font-size:18px;margin:20px 0px;text-align:left;">因此这里就得把这三种情况都考虑到,其他的以此类推。</span></p><p id="c14a313c-def3-411a-8a55-364985a19b8d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="637b74fa-e12b-4374-8498-a3e8e62ce5d8" style="font-size:18px;margin:20px 0px;text-align:left;">具体解析过程可以参考源码: https://github.com/crossoverJie/gjson/blob/main/parse.go</span></p><hr id="43ef69d2-0dad-437d-ba4f-a1325488ed75" style="font-size:18px;margin:20px 0px;text-align:left;"><p id="1a6d3885-f841-44e3-ab70-890203a2daeb" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="b10165f3-068e-4a04-ab6b-0446c3d6f251" style="font-size:18px;margin:20px 0px;text-align:left;">虽然是借助一个栈结构就能将</span><span id="e4f10672-44b9-40b8-8703-8ded5e4fda0c" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>解析完毕,不知道大家发现一个问题没有: 这样非常容易遗漏规则,比如刚才提到的一个冒号后面就有三种情况,而一个<span id="eb57d353-1089-4eab-8717-1eed3cbfe11f" style="font-size:18px;margin:20px 0px;text-align:left;">BeginArray</span>后甚至有四种情况(<span id="f3c50500-5024-4d81-8b85-cb83be279d4d" style="font-size:18px;margin:20px 0px;text-align:left;">StatusArrayValue, StatusBeginArray, StatusBeginObject, StatusEndArray</span>)</p><p id="a23c199c-8585-448b-a4ae-7816bd429c3f" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="b4f12850-cafb-4c53-8f03-136c94029c0e" style="font-size:18px;margin:20px 0px;text-align:left;">这样的代码读起来也不是很直观,同时容易遗漏语法,只能出现问题再进行修复。</span></p><p id="c003babf-301e-4aa0-8756-bed85a6c089d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="d1ca463d-4502-4d55-b991-fc5ab0540339" style="font-size:18px;margin:20px 0px;text-align:left;">既然提到了问题那自然也有相应的解决方案,其实就是语法分析中常见的递归下降算法。</span></p><div id="5c08715d-16f4-40ba-8ef2-8a0e3e915f18" style="font-size:18px;margin:20px 0px;text-align:left;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/75e8c39bf0fa48219c802e45a481aa83~noop.image?_iz=58558&amp;from=article.pc_detail&amp;x-expires=1660438836&amp;x-signature=OyjFL2UT5iMG07HDsvWedpSpPTY%3D" style="width:100%;"></div><p id="a6a4f155-4c11-491b-8fb1-064e9030ee5b" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="469549ef-55c3-4004-873e-ae565d841edc" style="font-size:18px;margin:20px 0px;text-align:left;">我们只需要根据</span><span id="7b929206-694c-4152-b310-24df04420bb7" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>的文法定义,递归的写出算法即可,这样代码阅读起来非常清晰,同时也不会遗漏规则。</p><p id="1df898c1-503b-48cb-b96b-1ac241bcf5fb" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="9eea1d3e-68e2-4550-a89d-d29b0d77a53d" style="font-size:18px;margin:20px 0px;text-align:left;">完整的</span><span id="87452871-2603-4f19-b21f-425f53f4fadc" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>语法查看这里: https://github.com/antlr/grammars-v4/blob/master/json/JSON.g4</p><p id="a80ed486-3863-4f68-a67b-d926aaa118f0" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="792c92ba-1c97-454d-83d0-d4e4af190418" style="font-size:18px;margin:20px 0px;text-align:left;">我也预计将下个版本改为递归下降算法来实现。</span></p><h1 id="54a8be5d-de07-495f-8a88-f79a5e2e5985" style="font-size:20px;margin:20px 0px;font-weight:700;">总结</h1><p id="4eb6804c-da33-4fec-a787-e75b3de92efa" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="163b53a0-0b24-420a-8f2b-fce75268b0b5" style="font-size:18px;margin:20px 0px;text-align:left;">当目前为止其实只是实现了一个非常基础的</span><span id="dddb08ed-c277-4633-b750-7f1a4295d0a4" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>解析,也没有做性能优化,和官方的<span id="d2806b2a-2f72-4bba-b07d-2a0a6d35cc5e" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>包对比性能差的不是一星半点。</p><pre id="2f29a36c-7546-40ff-af1c-991e7321a4c5" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="8c4bf71e-f978-4360-ad67-2e08d96d738e" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="852410a6-8863-4884-a021-12e38d8af67d" style="font-size:18px;margin:20px 0px;text-align:left;">cpu:</span><span id="370fee73-b8ef-46d1-8dae-2c5da307156c" style="font-size:18px;margin:20px 0px;text-align:left;">Intel(R)</span><span id="f4491734-10e8-4d49-bd51-1fd09629c388" style="font-size:18px;margin:20px 0px;text-align:left;">Core(TM)</span><span id="735034d6-7355-40de-895e-703ce9d63d98" style="font-size:18px;margin:20px 0px;text-align:left;">i7-9750H</span><span id="55c2937d-7e06-476f-a1d7-2ef5b44b7177" style="font-size:18px;margin:20px 0px;text-align:left;">CPU</span><span id="50d0dc88-5288-4fe1-8ef6-a821c2035d16" style="font-size:18px;margin:20px 0px;text-align:left;">@</span><span id="5f864372-07a6-4084-b4a2-4a5255071e5e" style="font-size:18px;margin:20px 0px;text-align:left;">2.</span><span id="c67203c9-0b8c-4d2b-9ad3-77988d4bb1ca" style="font-size:18px;margin:20px 0px;text-align:left;">60GHzBenchmarkJsonDecode-12</span><span id="3b0ba9d4-f8ab-44d5-82a3-d19bc98f11c2" style="font-size:18px;margin:20px 0px;text-align:left;">372298</span><span id="31945d84-5832-4cb1-b4e7-c42be30177e0" style="font-size:18px;margin:20px 0px;text-align:left;">15506</span><span id="c648d185-3fcc-4231-b36a-a3e2fc5fc3b1" style="font-size:18px;margin:20px 0px;text-align:left;">ns/op</span><span id="76f7e943-53cd-4520-bc58-633b46d39551" style="font-size:18px;margin:20px 0px;text-align:left;">512</span><span id="aefc3422-6a04-4633-9d5a-396b620aa366" style="font-size:18px;margin:20px 0px;text-align:left;">B/op</span><span id="0042bfac-b1e8-47d4-8663-fa6c9b76efb4" style="font-size:18px;margin:20px 0px;text-align:left;">12</span><span id="e7e93688-837c-41cd-afa4-264867fe6b54" style="font-size:18px;margin:20px 0px;text-align:left;">allocs/opBenchmarkDecode-12</span><span id="e5d6f966-c0e3-4e19-b17b-22eabf0fb3a6" style="font-size:18px;margin:20px 0px;text-align:left;">141482</span><span id="1ea60f26-ad9c-4ba5-b008-44eea519683d" style="font-size:18px;margin:20px 0px;text-align:left;">43516</span><span id="e55c8744-43f8-4e3a-a1e5-028ff012ff73" style="font-size:18px;margin:20px 0px;text-align:left;">ns/op</span><span id="3bd680eb-d238-437e-a231-f3cb6f8923cc" style="font-size:18px;margin:20px 0px;text-align:left;">30589</span><span id="6f1b3969-a7b1-4f7c-8bc7-a601da10c7bf" style="font-size:18px;margin:20px 0px;text-align:left;">B/op</span><span id="0cf992bb-5ed3-4cfe-a1d6-4c0ce619c5ee" style="font-size:18px;margin:20px 0px;text-align:left;">962</span><span id="2d1cd735-c081-4910-bee7-c458850fde60" style="font-size:18px;margin:20px 0px;text-align:left;">allocs/opPASS</span></code></pre><p id="dc063c55-2b10-416d-9e3c-2c2359eed48d" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="6b4fc1eb-03e7-4b27-a6ad-d9c218e0b0b0" style="font-size:18px;margin:20px 0px;text-align:left;">同时还有一些基础功能没有实现,比如将解析后的</span><span id="17f29556-828b-469b-915d-a38b0a8db66c" style="font-size:18px;margin:20px 0px;text-align:left;">JSONObject</span>可以反射生成自定义的<span id="8717e72e-b363-4471-ba77-12733ebaf8e1" style="font-size:18px;margin:20px 0px;text-align:left;">Struct</span>,以及我最终想实现的支持<span id="ad893cb9-f028-4528-859c-81060e26a9b0" style="font-size:18px;margin:20px 0px;text-align:left;">JSON</span>的四则运算:</p><pre id="59057fbe-c5dc-4cca-a719-ee12c1dfcb7f" style="font-size:18px;margin:20px 0px;text-align:left;"><code id="0593b9f4-dcf4-4628-b121-a3457e872fc9" style="font-size:18px;margin:20px 0px;text-align:left;"><span id="7927c7b4-3410-471b-9458-3c51ceb82d73" style="font-size:18px;margin:20px 0px;text-align:left;">gjson</span><span id="65c41665-ef56-4fd3-a97c-d03785348e9d" style="font-size:18px;margin:20px 0px;text-align:left;">.Get</span>("<span id="2169917a-704f-4d3e-b2c1-571ac97cb240" style="font-size:18px;margin:20px 0px;text-align:left;">glossary</span><span id="f5ea4adc-caa1-4a87-8900-ed09b53f2102" style="font-size:18px;margin:20px 0px;text-align:left;">.age</span>+<span id="12132c30-0101-4f50-89a8-a572dcf8e623" style="font-size:18px;margin:20px 0px;text-align:left;">long</span>*(<span id="9e19ce06-bddb-4321-b417-dac0f4d6b526" style="font-size:18px;margin:20px 0px;text-align:left;">a</span><span id="91670a48-515f-418b-b402-36e780f2db24" style="font-size:18px;margin:20px 0px;text-align:left;">.b</span>+<span id="ee221827-8b37-4062-bce9-511790d26cf2" style="font-size:18px;margin:20px 0px;text-align:left;">a</span><span id="545c4ba1-d129-49b0-b863-e437a3e9d9f9" style="font-size:18px;margin:20px 0px;text-align:left;">.c</span>)")</code></pre><p id="4cfad5a5-3297-40e8-8efd-3ce6b5093828" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="d92750d9-70bb-4b02-b8b5-3a548e89a02b" style="font-size:18px;margin:20px 0px;text-align:left;">目前我貌似没有发现有类似的库实现了这个功能,后面真的完成后应该会很有意思,感兴趣的朋友请持续关注。</span></p><p id="7d5834cf-2b01-4f9e-8dc0-23d858ef7f0a" style="font-weight:400;text-align:left;line-height:1.667;margin:20px 0px;font-size:18px;"><span id="961c4c5a-4f86-46d6-9a24-45b15b63a963" style="font-size:18px;margin:20px 0px;text-align:left;">源码:<a id="0ab8623c-3c26-4486-a00b-57e20e6d658e" style="font-size:18px;margin:20px 0px;text-align:left;">GitHub - crossoverJie/gjson: JSON parser for Go</a></span></p>
页: [1]
查看完整版本: 几百行代码实现一个JSON解析器