├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── images └── perf_minijson.png └── src ├── Global_AssemblyInfo.fs ├── MiniJson.CSharp ├── App.config ├── MiniJson.CSharp.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── MiniJson.NuGet ├── App.config ├── AssemblyInfo.fs ├── MiniJson.NuGet.fsproj ├── Program.fs └── packages.config ├── MiniJson.NuGetPackage └── M3.MiniJson.nuspec ├── MiniJson.Paket ├── App.config ├── AssemblyInfo.fs ├── MiniJson.Paket.fsproj ├── Program.fs ├── packages.config ├── paket-files │ └── raw.githubusercontent.com │ │ ├── MiniJson.Dynamic.fs │ │ ├── MiniJson.fs │ │ └── paket.version ├── paket.dependencies └── paket.lock ├── MiniJson.Reference ├── App.config ├── AssemblyInfo.fs ├── MiniJson.Reference.fsproj ├── Reference.fs └── packages.config ├── MiniJson.Tests ├── App.config ├── AssemblyInfo.fs ├── FSharpData.fs ├── Jil.fs ├── JsonNet.fs ├── MiniJson.Tests.fsproj ├── Program.fs ├── Test.fs ├── TestCases.fs ├── TestCases │ ├── Dates.json │ ├── DoubleNested.json │ ├── Empty.json │ ├── GitHub.json │ ├── Nested.json │ ├── OptionValues.json │ ├── Simple.json │ ├── SimpleArray.json │ ├── TypeInference.json │ ├── Vindinium.json │ ├── WikiData.json │ ├── WorldBank.json │ ├── charrefs-full.json │ ├── charrefs.json │ ├── contacts.json │ ├── optionals.json │ ├── projects.json │ ├── reddit.json │ └── topic.json ├── coverage.cmd └── packages.config ├── MiniJson.sln └── MiniJson ├── App.config ├── AssemblyInfo.fs ├── MiniJson.Adaptor.fs ├── MiniJson.Dynamic.fs ├── MiniJson.fs ├── MiniJson.fsproj └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | 10 | packages/ 11 | *.nupkg 12 | results.xml 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | x64/ 19 | build/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Roslyn cache directories 25 | *.ide/ 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | #NUNIT 32 | *.VisualState.xml 33 | TestResult.xml 34 | 35 | # Build Results of an ATL Project 36 | [Dd]ebugPS/ 37 | [Rr]eleasePS/ 38 | dlldata.c 39 | 40 | *_i.c 41 | *_p.c 42 | *_i.h 43 | *.ilk 44 | *.meta 45 | *.obj 46 | *.pch 47 | *.pdb 48 | *.pgc 49 | *.pgd 50 | *.rsp 51 | *.sbr 52 | *.tlb 53 | *.tli 54 | *.tlh 55 | *.tmp 56 | *.tmp_proj 57 | *.log 58 | *.vspscc 59 | *.vssscc 60 | .builds 61 | *.pidb 62 | *.svclog 63 | *.scc 64 | 65 | # Chutzpah Test files 66 | _Chutzpah* 67 | 68 | # Visual C++ cache files 69 | ipch/ 70 | *.aps 71 | *.ncb 72 | *.opensdf 73 | *.sdf 74 | *.cachefile 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | *.vspx 80 | 81 | # TFS 2012 Local Workspace 82 | $tf/ 83 | 84 | # Guidance Automation Toolkit 85 | *.gpState 86 | 87 | # ReSharper is a .NET coding add-in 88 | _ReSharper*/ 89 | *.[Rr]e[Ss]harper 90 | *.DotSettings.user 91 | 92 | # JustCode is a .NET coding addin-in 93 | .JustCode 94 | 95 | # TeamCity is a build add-in 96 | _TeamCity* 97 | 98 | # DotCover is a Code Coverage Tool 99 | *.dotCover 100 | 101 | # NCrunch 102 | _NCrunch_* 103 | .*crunch*.local.xml 104 | 105 | # MightyMoose 106 | *.mm.* 107 | AutoTest.Net/ 108 | 109 | # Web workbench (sass) 110 | .sass-cache/ 111 | 112 | # Installshield output folder 113 | [Ee]xpress/ 114 | 115 | # DocProject is a documentation generator add-in 116 | DocProject/buildhelp/ 117 | DocProject/Help/*.HxT 118 | DocProject/Help/*.HxC 119 | DocProject/Help/*.hhc 120 | DocProject/Help/*.hhk 121 | DocProject/Help/*.hhp 122 | DocProject/Help/Html2 123 | DocProject/Help/html 124 | 125 | # Click-Once directory 126 | publish/ 127 | 128 | # Publish Web Output 129 | *.[Pp]ublish.xml 130 | *.azurePubxml 131 | ## TODO: Comment the next line if you want to checkin your 132 | ## web deploy settings but do note that will include unencrypted 133 | ## passwords 134 | #*.pubxml 135 | 136 | # NuGet Packages Directory 137 | packages/* 138 | ## TODO: If the tool you use requires repositories.config 139 | ## uncomment the next line 140 | #!packages/repositories.config 141 | 142 | # Enable "build/" folder in the NuGet Packages folder since 143 | # NuGet packages use it for MSBuild targets. 144 | # This line needs to be after the ignore of the build folder 145 | # (and the packages folder if the line above has been uncommented) 146 | !packages/build/ 147 | 148 | # Windows Azure Build Output 149 | csx/ 150 | *.build.csdef 151 | 152 | # Windows Store app package directory 153 | AppPackages/ 154 | 155 | # Others 156 | sql/ 157 | *.Cache 158 | ClientBin/ 159 | [Ss]tyle[Cc]op.* 160 | ~$* 161 | *~ 162 | *.dbmdl 163 | *.dbproj.schemaview 164 | *.pfx 165 | *.publishsettings 166 | node_modules/ 167 | bower_components/ 168 | 169 | # RIA/Silverlight projects 170 | Generated_Code/ 171 | 172 | # Backup & report files from converting an old project file 173 | # to a newer Visual Studio version. Backup files are not needed, 174 | # because we have git ;-) 175 | _UpgradeReport_Files/ 176 | Backup*/ 177 | UpgradeLog*.XML 178 | UpgradeLog*.htm 179 | 180 | # SQL Server files 181 | *.mdf 182 | *.ldf 183 | 184 | # Business Intelligence projects 185 | *.rdl.data 186 | *.bim.layout 187 | *.bim_*.settings 188 | 189 | # Microsoft Fakes 190 | FakesAssemblies/ 191 | 192 | # LightSwitch generated files 193 | GeneratedArtifacts/ 194 | _Pvt_Extensions/ 195 | ModelManifest.xml 196 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: MiniJson.sln 3 | sudo: false 4 | dist: trusty 5 | branches: 6 | only: 7 | - master 8 | mono: 9 | - latest 10 | before_install: 11 | - cd ./src 12 | script: 13 | - xbuild /p:Configuration=Debug MiniJson.sln 14 | - mono ./MiniJson.Tests/bin/Debug/MiniJson.Tests.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniJson 2 | 3 | [![Build Status](https://travis-ci.org/mrange/MiniJson.svg?branch=master)](https://travis-ci.org/mrange/MiniJson) 4 | 5 | MiniJson is a [conforming](http://jsonlint.com) [JSON](http://json.org) parser for F# licensed under 6 | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 7 | 8 | MiniJson has decent performance (compares favourable to [Json.NET](http://www.newtonsoft.com/json) and [FSharp.Data](https://github.com/fsharp/FSharp.Data)) 9 | and provides decent error messages (possible to suppress if performance is of importance). 10 | 11 | ![Deserialization Performance Comparison (lower is better)](https://raw.githubusercontent.com/mrange/MiniJson/master/images/perf_minijson.png) 12 | MiniJson deserialization compared to popular JSON libraries. Lower is better. 13 | 14 | 15 | Example of error message when trying to parse an invalid JSON document: "{"abc":}" 16 | ``` 17 | Failed to parse input as JSON 18 | {"abc":} 19 | -------^ Pos: 7 20 | Expected: '"', '-', '[', '{', digit, false, null or true 21 | ``` 22 | 23 | The best way of referencing MiniJson is to use [Paket](http://www.nuget.org/packages/Paket/) 24 | [http references](http://fsprojects.github.io/Paket/http-dependencies.html) 25 | 26 | [paket.dependencies](http://fsprojects.github.io/Paket/dependencies-file.html) 27 | ``` 28 | http https://raw.githubusercontent.com/mrange/MiniJson/master/src/MiniJson/MiniJson.fs 29 | http https://raw.githubusercontent.com/mrange/MiniJson/master/src/MiniJson/MiniJson.Dynamic.fs 30 | ``` 31 | 32 | [NuGet](http://www.nuget.org/packages/M3.MiniJson/) can also be used to reference MiniJson 33 | 34 | To install MiniJson using NuGet, run the following command in the [Package Manager Console](http://docs.nuget.org/consume/package-manager-console) 35 | ``` 36 | PM> Install-Package M3.MiniJson 37 | ``` 38 | 39 | Using MiniJson is straight-forward (F#) 40 | ```fsharp 41 | open MiniJson.JsonModule 42 | open MiniJson.DynamicJsonModule 43 | 44 | [] 45 | let main argv = 46 | let jsonText = """[{"id":"123", "name":"Mr. Big", "age":30}, {"id":"123", "name":"Mr. X"}]""" 47 | 48 | match parse true jsonText with // true for full error-info 49 | | Failure (msg, pos) -> printfn "Failure@%d\n%s" pos msg 50 | | Success json -> 51 | printfn "Success\n%s" <| toString true json // true to indent JSON 52 | 53 | let root = json.Query 54 | 55 | for i = 0 to root.Length - 1 do 56 | let v = root.[i] 57 | let id = v?id.AsString 58 | let name = v?name.AsString 59 | let age = v?age.AsFloat 60 | printfn "Record - %d: id:%s, name:%s, age:%f" i id name age 61 | 0 62 | ``` 63 | 64 | Even though MiniJson is primarily designed with F# in mind there are adaptor 65 | functionality to make MiniJson usable from C#/VB 66 | ```csharp 67 | using System; 68 | 69 | using MiniJson.Adaptor; 70 | 71 | class Program 72 | { 73 | static void Main(string[] args) 74 | { 75 | var jsonText = @"[{""id"":""123"", ""name"":""Mr. Big"", ""age"":30}, {""id"":""123"", ""name"":""Mr. X""}]"; 76 | 77 | var jsonParser = new JsonParser (jsonText, true); 78 | 79 | Console.WriteLine ("ParseResult: {0}", jsonParser); 80 | 81 | dynamic[] users = jsonParser.DynamicResult.GetChildren (); 82 | 83 | foreach (dynamic user in users) 84 | { 85 | string id = user.id ; 86 | string name = user.name ; 87 | double age = user.age.ConvertToFloat (-1.0); 88 | Console.WriteLine ("Record: id:{0}, name:{1}, age:{2}", id, name, age); 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | 95 | # TODO 96 | 97 | 1. Migrate to F# project scaffold 98 | 1. Add MiniJson Strong Name 99 | 100 | -------------------------------------------------------------------------------- /images/perf_minijson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrange/MiniJson/067b135a85deb40e51e5c786fda177f74f838995/images/perf_minijson.png -------------------------------------------------------------------------------- /src/Global_AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.CompilerServices 20 | open System.Runtime.InteropServices 21 | 22 | [] 23 | [] 24 | [] 25 | [] 26 | [] 27 | [] 28 | [] 29 | 30 | [] 31 | 32 | [] 33 | [] 34 | do 35 | () 36 | -------------------------------------------------------------------------------- /src/MiniJson.CSharp/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/MiniJson.CSharp/MiniJson.CSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {ADF5BA44-E250-4C08-865E-793C009C9A5B} 8 | Exe 9 | Properties 10 | MiniJson.CSharp 11 | MiniJson.CSharp 12 | v4.5 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | false 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | false 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {68c7233e-b616-4233-a3b8-6881f11dfb2f} 54 | MiniJson 55 | 56 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /src/MiniJson.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the ""License""); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an ""AS IS"" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | 17 | namespace MiniJson.Adaptor.Tests 18 | { 19 | using System; 20 | 21 | using MiniJson.Adaptor; 22 | 23 | class Program 24 | { 25 | static void Main(string[] args) 26 | { 27 | var jsonText = @"[{""id"":""123"", ""name"":""Mr. Big"", ""age"":30}, {""id"":""123"", ""name"":""Mr. X""}]"; 28 | 29 | var jsonParser = new JsonParser (jsonText, true); 30 | 31 | Console.WriteLine ("ParseResult: {0}", jsonParser); 32 | 33 | dynamic[] users = jsonParser.DynamicResult.GetChildren (); 34 | 35 | foreach (dynamic user in users) 36 | { 37 | string id = user.id ; 38 | string name = user.name ; 39 | double age = user.age.ConvertToFloat (-1.0); 40 | Console.WriteLine ("Record: id:{0}, name:{1}, age:{2}", id, name, age); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/MiniJson.CSharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | using System.Reflection; 17 | using System.Runtime.InteropServices; 18 | 19 | [assembly: AssemblyTitle("MiniJson.CSharp")] 20 | [assembly: AssemblyDescription("MiniJson is a conforming Json library licensed under Apache License, Version 2.0")] 21 | [assembly: AssemblyConfiguration("")] 22 | [assembly: AssemblyCompany("")] 23 | [assembly: AssemblyProduct("MiniJson")] 24 | [assembly: AssemblyCopyright("Copyright © Mårten Rånge 2015")] 25 | [assembly: AssemblyTrademark("")] 26 | [assembly: AssemblyCulture("")] 27 | 28 | [assembly: ComVisible(false)] 29 | 30 | [assembly: Guid("adf5ba44-e250-4c08-865e-793c009c9a5b")] 31 | 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /src/MiniJson.NuGet/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/MiniJson.NuGet/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.InteropServices 20 | 21 | [] 22 | [] 23 | do 24 | () 25 | 26 | -------------------------------------------------------------------------------- /src/MiniJson.NuGet/MiniJson.NuGet.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 80bdd5ea-bd91-479b-8d6f-1dcd1a18f49a 9 | Exe 10 | MiniJson.NuGet 11 | MiniJson.NuGet 12 | v4.5 13 | true 14 | 4.4.0.0 15 | MiniJson.NuGet 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | 5 27 | AnyCPU 28 | bin\Debug\MiniJson.NuGet.XML 29 | true 30 | 31 | 32 | pdbonly 33 | true 34 | true 35 | bin\Release\ 36 | TRACE 37 | 5 38 | AnyCPU 39 | bin\Release\MiniJson.NuGet.XML 40 | true 41 | 42 | 43 | 11 44 | 45 | 46 | true 47 | full 48 | false 49 | false 50 | bin\Debug\ 51 | DEBUG;TRACE 52 | 5 53 | bin\Debug\MiniJson.NuGet.XML 54 | true 55 | x64 56 | 57 | 58 | pdbonly 59 | true 60 | true 61 | bin\Release\ 62 | TRACE 63 | 5 64 | bin\Release\MiniJson.NuGet.XML 65 | true 66 | x64 67 | 68 | 69 | true 70 | full 71 | false 72 | false 73 | bin\Debug\ 74 | DEBUG;TRACE 75 | 5 76 | bin\Debug\MiniJson.NuGet.XML 77 | true 78 | x86 79 | 80 | 81 | pdbonly 82 | true 83 | true 84 | bin\Release\ 85 | TRACE 86 | 5 87 | bin\Release\MiniJson.NuGet.XML 88 | true 89 | x86 90 | 91 | 92 | 93 | 94 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 95 | 96 | 97 | 98 | 99 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 100 | 101 | 102 | 103 | 104 | 105 | 106 | Global_AssemblyInfo.fs 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ..\packages\M3.MiniJson.2.3.0\lib\net40-client\MiniJson.dll 116 | True 117 | 118 | 119 | 120 | FSharp.Core 121 | FSharp.Core.dll 122 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll 123 | 124 | 125 | 126 | 127 | 128 | 135 | -------------------------------------------------------------------------------- /src/MiniJson.NuGet/Program.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | open MiniJson.JsonModule 17 | open MiniJson.DynamicJsonModule 18 | 19 | [] 20 | let main argv = 21 | let jsonText = """[{"id":"123", "name":"Mr. Big", "age":30}, {"id":"123", "name":"Mr. X"}]""" 22 | 23 | match parse true jsonText with // true for full error-info 24 | | Failure (msg, pos) -> printfn "Failure@%d\n%s" pos msg 25 | | Success json -> 26 | printfn "Success\n%s" <| toString true json // true to indent JSON 27 | 28 | let root = json.Query 29 | 30 | // F#4.1 regression workaround: https://github.com/Microsoft/visualfsharp/issues/2416 31 | let inline ( ? ) (x : JsonPath) name = x.Get name 32 | 33 | for i = 0 to root.Length - 1 do 34 | let v = root.[i] 35 | let id = v?id.AsString 36 | let name = v?name.AsString 37 | let age = v?age.AsFloat 38 | printfn "Record - %d: id:%s, name:%s, age:%f" i id name age 39 | 0 40 | -------------------------------------------------------------------------------- /src/MiniJson.NuGet/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MiniJson.NuGetPackage/M3.MiniJson.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | M3.MiniJson 5 | 2.3.1 6 | MiniJson 7 | Mårten Rånge 8 | Mårten Rånge 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | https://github.com/mrange/MiniJson 11 | false 12 | 13 | MiniJson aims to be a functionally idiomatic, conforming (http://json.org/) and 14 | performing JSON (de)serializer that provides decent error reporting on parse failures. 15 | 16 | MiniJson is a JSON library for F# licensed under Apache License, Version 2.0 17 | parser json F# fsharp 18 | 19 | 20 | 21 | * 2.3.1 - Formats floats with 'G' 22 | * 2.3.0 - Added C#/VB dynamic adaptor 23 | * 2.2.5 - toString now handles Nan and Infs 24 | * 2.2.4 - Improved handling of numbers near to 0 or "near" to Infinity 25 | * 2.2.3 - Improved dynamic path, improved error reporting at EOS 26 | * 2.2.2 - Improved toString wrt non-printable characters 27 | * 2.2.1 - Improved string performance, fixed truncated token issue 28 | * 2.2.0 - Removed unsafe methods. Minor perf and memory improvements. 29 | * 2.1.2 - MiniJson now requires net40-client (down from net452) 30 | * 2.1.1 - Improved code documentation substantially 31 | * 2.1.0 - Improved performance (breaking change for users of IParseVisitor) 32 | * 2.0.1 - Documentation update 33 | * 2.0.0 - Internalizing internal parser details (breaking change for users relying on internal parser details) 34 | * 1.0.0 - Initial release 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.InteropServices 20 | 21 | [] 22 | [] 23 | do 24 | () -------------------------------------------------------------------------------- /src/MiniJson.Paket/MiniJson.Paket.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | c3711d8d-21a1-4e9a-b130-f4b4193c24e6 9 | Exe 10 | MiniJson.Paket 11 | MiniJson.Paket 12 | v4.5 13 | true 14 | 4.4.0.0 15 | MiniJson.Paket 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | false 24 | bin\Debug\ 25 | TRACE;DEBUG;INTERNAL_MINIJSON_WORKAROUND 26 | 5 27 | AnyCPU 28 | bin\Debug\MiniJson.Paket.XML 29 | true 30 | 31 | 32 | pdbonly 33 | true 34 | true 35 | bin\Release\ 36 | TRACE;INTERNAL_MINIJSON_WORKAROUND 37 | 5 38 | AnyCPU 39 | bin\Release\MiniJson.Paket.XML 40 | true 41 | 42 | 43 | 11 44 | 45 | 46 | true 47 | full 48 | false 49 | false 50 | bin\Debug\ 51 | TRACE;DEBUG;INTERNAL_MINIJSON_WORKAROUND 52 | 5 53 | bin\Debug\MiniJson.Paket.XML 54 | true 55 | x64 56 | 57 | 58 | pdbonly 59 | true 60 | true 61 | bin\Release\ 62 | TRACE;INTERNAL_MINIJSON_WORKAROUND 63 | 5 64 | bin\Release\MiniJson.Paket.XML 65 | true 66 | x64 67 | 68 | 69 | true 70 | full 71 | false 72 | false 73 | bin\Debug\ 74 | TRACE;DEBUG;INTERNAL_MINIJSON_WORKAROUND 75 | 5 76 | bin\Debug\MiniJson.Paket.XML 77 | true 78 | x86 79 | 80 | 81 | pdbonly 82 | true 83 | true 84 | bin\Release\ 85 | TRACE;INTERNAL_MINIJSON_WORKAROUND 86 | 5 87 | bin\Release\MiniJson.Paket.XML 88 | true 89 | x86 90 | 91 | 92 | 93 | 94 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 95 | 96 | 97 | 98 | 99 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 100 | 101 | 102 | 103 | 104 | 105 | 106 | Global_AssemblyInfo.fs 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | FSharp.Core 121 | FSharp.Core.dll 122 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll 123 | 124 | 125 | 126 | 127 | 128 | 135 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/Program.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | open Internal.MiniJson.JsonModule 17 | open Internal.MiniJson.DynamicJsonModule 18 | 19 | [] 20 | let main argv = 21 | let jsonText = """[{"id":"123", "name":"Mr. Big", "age":30}, {"id":"123", "name":"Mr. X"}]""" 22 | 23 | match parse true jsonText with // true for full error-info 24 | | Failure (msg, pos) -> printfn "Failure@%d\n%s" pos msg 25 | | Success json -> 26 | printfn "Success\n%s" <| toString true json // true to indent JSON 27 | 28 | let root = json.Query 29 | 30 | // F#4.1 regression workaround: https://github.com/Microsoft/visualfsharp/issues/2416 31 | let inline ( ? ) (x : JsonPath) name = x.Get name 32 | 33 | for i = 0 to root.Length - 1 do 34 | let v = root.[i] 35 | let id = v?id.AsString 36 | let name = v?name.AsString 37 | let age = v?age.AsFloat 38 | printfn "Record - %d: id:%s, name:%s, age:%f" i id name age 39 | 0 40 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/paket-files/raw.githubusercontent.com/MiniJson.Dynamic.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | 17 | /// MiniJson aims to be a minimal yet conforming JSON parser with reasonable performance and decent error reporting 18 | /// JSON Specification: http://json.org/ 19 | /// JSON Lint : http://jsonlint.com/ 20 | /// 21 | /// MiniJson.DynamicJsonModule contains functionality query a JSON document using 22 | /// op_Dynamic ( ? ) in F# 23 | /// 24 | /// 25 | /// Example: 26 | /// -------- 27 | /// let root = json.Query 28 | /// 29 | /// for i = 0 to root.Length - 1 do 30 | /// let v = root.[i] 31 | /// let id = v?id.AsString 32 | /// let name = v?name.AsString 33 | /// let age = v?age.AsFloat 34 | /// printfn "Record - %d: id:%s, name:%s, age:%f" i id name age 35 | #if PUBLIC_MINIJSON 36 | module MiniJson.DynamicJsonModule 37 | #else 38 | // Due to what seems to be an issue with the F# compiler preventing 39 | // access to internal operator ? from within the same assembly 40 | // define INTERNAL_MINIJSON_WORKAROUND to suppress internalizing of 41 | // MiniJson. 42 | #if INTERNAL_MINIJSON_WORKAROUND 43 | module Internal.MiniJson.DynamicJsonModule 44 | #else 45 | module internal Internal.MiniJson.DynamicJsonModule 46 | #endif 47 | #endif 48 | open System 49 | open System.Globalization 50 | open System.Text 51 | 52 | open JsonModule 53 | 54 | /// Represents a valid JSON element query 55 | type JsonQuery = 56 | /// (name) - Represents a valid JSON element object property query 57 | | QueryProperty of string 58 | /// (index) - Represents a valid JSON element array indexing query 59 | | QueryIndexOf of int 60 | 61 | /// Represents an invalid JSON element query 62 | type JsonQueryError = 63 | /// (name) - Represents a invalid JSON element object property query as the referenced element wasn't an object 64 | | ErrorNotObject of string 65 | /// (name) - Represents a invalid JSON element array indexing query as the referenced element wasn't an array 66 | | ErrorNotIndexable of int 67 | /// (name) - Represents a invalid JSON element object property query as the property didn't exist 68 | | ErrorUnknownProperty of string 69 | /// (name) - Represents a invalid JSON element array indexing query as the index was out of bounds 70 | | ErrorIndexOutBounds of int 71 | 72 | /// (json, parents) - Represents a valid Path to a JSON element 73 | type Path = Json*(JsonQuery*Json) list 74 | 75 | /// (errors, json, parents) - Represents an invalid Path to a JSON element 76 | type InvalidPath = JsonQueryError list*Json*(JsonQuery*Json) list 77 | 78 | module internal Details = 79 | let inline ch (sb : StringBuilder) (c : char) : unit = ignore <| sb.Append c 80 | let inline str (sb : StringBuilder) (s : string) : unit = ignore <| sb.Append s 81 | let inline ii (sb : StringBuilder) (i : int) : unit = ignore <| sb.Append i 82 | 83 | let rec appendParents (sb : StringBuilder) = function 84 | | [] -> () 85 | | p::ps -> 86 | // Tail-recursiveness not so important here as we expect only a few parents 87 | appendParents sb ps 88 | match p with 89 | | (QueryProperty name, _) -> ch sb '.'; str sb name 90 | | (QueryIndexOf i, _) -> str sb ".["; ii sb i; ch sb ']' 91 | 92 | let rec appendErrors (sb : StringBuilder) = function 93 | | [] -> () 94 | | e::es -> 95 | // Tail-recursiveness not so important here as we expect only a few errors 96 | appendErrors sb es 97 | match e with 98 | | ErrorNotObject name 99 | | ErrorUnknownProperty name -> ch sb '!'; str sb name 100 | | ErrorNotIndexable i 101 | | ErrorIndexOutBounds i -> str sb "!["; ii sb i; ch sb ']' 102 | 103 | open Details 104 | 105 | [] 106 | [] 107 | /// Represents a JSON scalar (null, bool, number, string or error) 108 | type JsonScalar = 109 | /// (json, parents) - Represents a null scalar 110 | | ScalarNull of Path 111 | /// (json, parents) - Represents a bool scalar 112 | | ScalarBoolean of Path*bool 113 | /// (json, parents) - Represents a number (float) scalar 114 | | ScalarNumber of Path*float 115 | /// (json, parents) - Represents a string scalar 116 | | ScalarString of Path*string 117 | /// (json, parents) - An errors representing that the referenced element is not a scalar 118 | | ScalarNotScalar of Path 119 | /// (json, parents) - An errors representing that the referenced element doesn't exist 120 | | ScalarInvalidPath of InvalidPath 121 | 122 | /// Returns true if it's an invalid scalar element 123 | member x.IsError : bool = 124 | match x with 125 | | ScalarNull _ 126 | | ScalarBoolean _ 127 | | ScalarNumber _ 128 | | ScalarString _ -> false 129 | | ScalarNotScalar _ 130 | | ScalarInvalidPath _ -> true 131 | 132 | /// Returns true if it's a valid scalar element, 133 | /// if the scalar element couldn't be converted successfully returns false. 134 | member x.HasValue : bool = 135 | match x with 136 | | ScalarNull _ -> false 137 | | ScalarBoolean _ 138 | | ScalarNumber _ 139 | | ScalarString _ -> true 140 | | ScalarNotScalar _ 141 | | ScalarInvalidPath _ -> false 142 | 143 | /// Returns a boolean representation of the scalar element, 144 | /// if the scalar element couldn't be converted successfully returns false. 145 | member x.AsBool : bool = 146 | match x with 147 | | ScalarNull _ -> false 148 | | ScalarBoolean (_,b) -> b 149 | | ScalarNumber (_,n) -> n <> 0.0 150 | | ScalarString (_,s) -> s.Length > 0 151 | | ScalarNotScalar _ 152 | | ScalarInvalidPath _ -> false 153 | 154 | /// Returns a float representation of the scalar element, 155 | /// if the scalar element couldn't be converted successfully returns 0.0. 156 | member x.AsFloat : float = 157 | x.ConvertToFloat 0. 158 | 159 | /// Returns a string representation of the scalar element. 160 | /// Null values and errors are represented as "" 161 | member x.AsString : string = 162 | x.AsStringImpl false 163 | 164 | /// Returns the expanded string representation of the scalar element. 165 | /// Expanded means null values are represented as 'null' and full error error infos are generated for errors 166 | member x.AsExpandedString : string = 167 | x.AsStringImpl true 168 | 169 | /// Returns a float representation of the scalar element. 170 | /// This allows the user to specify the value to return if the scalar element couldn't be converted successfully 171 | member x.ConvertToFloat (defaultTo : float) : float = 172 | match x with 173 | | ScalarNull _ -> 0. 174 | | ScalarBoolean (_,b) -> if b then 1. else 0. 175 | | ScalarNumber (_,n) -> n 176 | | ScalarString (_,s) -> 177 | let b,f = Double.TryParse (s, NumberStyles.Float, CultureInfo.InvariantCulture) 178 | if b then f else defaultTo 179 | | ScalarNotScalar _ 180 | | ScalarInvalidPath _ -> defaultTo 181 | 182 | member internal x.AsStringImpl (expand : bool) : string = 183 | match x with 184 | | ScalarNull _ -> if expand then "null" else "" 185 | | ScalarBoolean (_,b) -> if b then "true" else "false" 186 | | ScalarNumber (_,n) -> n.ToString CultureInfo.InvariantCulture 187 | | ScalarString (_,s) -> s 188 | | ScalarNotScalar path -> 189 | if expand then 190 | let json, parents = path 191 | let sb = StringBuilder ("NotScalar: root") 192 | appendParents sb parents 193 | sb.ToString () 194 | else 195 | "" 196 | | ScalarInvalidPath invalidPath -> 197 | if expand then 198 | let errors, json, parents = invalidPath 199 | let sb = StringBuilder ("InvalidPath: root") 200 | appendParents sb parents 201 | appendErrors sb errors 202 | sb.ToString () 203 | else 204 | "" 205 | 206 | override x.ToString () : string = 207 | x.AsStringImpl true 208 | 209 | /// Represents a path to a JSON element 210 | type JsonPath = 211 | /// (json, parents) - Holds the current json element and its parents 212 | | PathOk of Path 213 | /// (errors, json, parents) - Holds the invalid path elements, the last valid json element and its parents 214 | | PathError of InvalidPath 215 | 216 | /// Evaluates the path producing the referenced scalar element (scalar meaning null, bool, number, string or error) 217 | member x.Eval : JsonScalar = 218 | match x with 219 | | PathOk path -> 220 | let json, _ = path 221 | match json with 222 | | JsonNull -> ScalarNull path 223 | | JsonBoolean b -> ScalarBoolean (path, b) 224 | | JsonNumber n -> ScalarNumber (path, n) 225 | | JsonString s -> ScalarString (path, s) 226 | | _ -> ScalarNotScalar path 227 | | PathError invalidPath -> ScalarInvalidPath invalidPath 228 | 229 | /// Returns the Length of the referenced array element, 230 | /// if it's not an array returns 0 231 | member x.Length : int = 232 | match x with 233 | | PathOk (json, _) -> 234 | match json with 235 | | JsonNull 236 | | JsonBoolean _ 237 | | JsonNumber _ 238 | | JsonString _ -> 0 239 | | JsonObject ms -> ms.Length 240 | | JsonArray vs -> vs.Length 241 | | PathError _ -> 0 242 | 243 | /// Returns a path to the element at the index of the referenced array element, 244 | /// if it's not an array or out of bounds returns a PathError 245 | member x.Item (i : int) : JsonPath = 246 | match x with 247 | | PathOk (json, parents) -> 248 | match json with 249 | | JsonNull 250 | | JsonBoolean _ 251 | | JsonNumber _ 252 | | JsonString _ -> 253 | PathError ([ErrorNotIndexable i], json, parents) 254 | | JsonObject ms -> 255 | if i >= 0 && i < ms.Length then 256 | let _, v = ms.[i] 257 | PathOk (v, (QueryIndexOf i, json)::parents) 258 | else 259 | PathError ([ErrorIndexOutBounds i], json, parents) 260 | | JsonArray vs -> 261 | if i >= 0 && i < vs.Length then 262 | let v = vs.[i] 263 | PathOk (v, (QueryIndexOf i, json)::parents) 264 | else 265 | PathError ([ErrorIndexOutBounds i], json, parents) 266 | | PathError (errors, json, parents) -> 267 | PathError ((ErrorNotIndexable i)::errors, json, parents) 268 | 269 | 270 | /// Returns all property names in order (and with potential duplicates) 271 | /// if it's not an object or named element doesn't exists returns an empty array 272 | member x.Names : string [] = 273 | match x with 274 | | PathOk (json, parents) -> 275 | match json with 276 | | JsonNull 277 | | JsonBoolean _ 278 | | JsonNumber _ 279 | | JsonString _ 280 | | JsonArray _ -> 281 | [||] 282 | | JsonObject ms -> 283 | ms |> Array.map (fun (k,_) -> k) 284 | | PathError (errors, json, parents) -> 285 | [||] 286 | 287 | /// Returns all children as an array of values in order (and with potential duplicates) 288 | /// if it's not an object, array or named element doesn't exists returns an empty array 289 | member x.Children : JsonPath [] = 290 | match x with 291 | | PathOk (json, parents) -> 292 | match json with 293 | | JsonNull 294 | | JsonBoolean _ 295 | | JsonNumber _ 296 | | JsonString _ -> 297 | [||] 298 | | JsonArray vs -> 299 | vs |> Array.mapi (fun i v -> PathOk (v, (QueryIndexOf i, json)::parents)) 300 | | JsonObject ms -> 301 | ms |> Array.mapi (fun i (_,v) -> PathOk (v, (QueryIndexOf i, json)::parents)) 302 | | PathError (errors, json, parents) -> 303 | [||] 304 | 305 | 306 | /// Returns a path to the named element of the referenced object element, 307 | /// if it's not an object or named element doesn't exists returns a PathError 308 | member x.Get (name : string) : JsonPath = 309 | match x with 310 | | PathOk (json, parents) -> 311 | match json with 312 | | JsonNull 313 | | JsonBoolean _ 314 | | JsonNumber _ 315 | | JsonString _ 316 | | JsonArray _ -> 317 | PathError ([ErrorNotObject name], json, parents) 318 | | JsonObject ms -> 319 | let rec find i = 320 | if i < ms.Length then 321 | let k,v = ms.[i] 322 | if k = name then 323 | PathOk (v, (QueryProperty name ,json)::parents) 324 | else 325 | find (i + 1) 326 | else 327 | PathError ([ErrorUnknownProperty name], json, parents) 328 | find 0 329 | | PathError (errors, json, parents) -> 330 | PathError ((ErrorNotObject name)::errors, json, parents) 331 | 332 | /// Returns true if the path elements references a valid scalar element, 333 | /// if the referenced scalar element couldn't be converted successfully returns false. 334 | member x.HasValue : bool = 335 | x.Eval.HasValue 336 | 337 | /// Returns a boolean representation of the referenced scalar element, 338 | /// if the referenced scalar element couldn't be converted successfully returns false. 339 | member x.AsBool : bool = 340 | x.Eval.AsBool 341 | 342 | /// Returns a float representation of the referenced scalar element, 343 | /// if the referenced scalar element couldn't be converted successfully returns 0.0. 344 | member x.AsFloat : float = 345 | x.Eval.AsFloat 346 | 347 | /// Returns a string representation of the referenced scalar element, 348 | /// null values and errors are represented as "". 349 | member x.AsString : string = 350 | x.Eval.AsString 351 | 352 | /// Returns the expanded string representation of the referenced scalar element. 353 | /// Expanded means null values are represented as 'null' and full error error infos are generated for errors. 354 | member x.AsExpandedString : string = 355 | x.Eval.AsExpandedString 356 | 357 | /// Returns a float representation of the referenced scalar element. 358 | /// This allows the user to specify the value to return if the referenced scalar element couldn't be converted successfully. 359 | /// @defaultTo - The float to default to if the referenced scalar element couldn't be converted successfully. 360 | member x.ConvertToFloat (defaultTo : float) : float = 361 | x.Eval.ConvertToFloat defaultTo 362 | 363 | override x.ToString () : string = 364 | match x with 365 | | PathOk (_, parents) -> 366 | let sb = StringBuilder ("PathOk: root") 367 | appendParents sb parents 368 | sb.ToString () 369 | | PathError (errors, _, parents) -> 370 | let sb = StringBuilder ("PathError: root") 371 | appendParents sb parents 372 | appendErrors sb errors 373 | sb.ToString () 374 | 375 | /// Returns a path to the named element of the referenced object element, 376 | /// if it's not an object or named element doesn't exists returns a PathError 377 | static member inline ( ? ) (path : JsonPath, name : string) : JsonPath = 378 | path.Get name 379 | 380 | /// Evaluates the path producing the referenced scalar element (scalar meaning null, bool, number, string or error) 381 | static member inline ( !! ) (path : JsonPath) : JsonScalar = 382 | path.Eval 383 | 384 | /// Creates a JsonPath object from a JSON document 385 | /// @json - A JSON document 386 | let inline makePath (json : Json) = PathOk (json, []) 387 | 388 | type Json with 389 | 390 | /// Creates a JsonPath object from a JSON document 391 | member x.Query : JsonPath = makePath x 392 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/paket-files/raw.githubusercontent.com/paket.version: -------------------------------------------------------------------------------- 1 | /mrange/MiniJson/master/src/MiniJson/MiniJson.Dynamic.fs 2 | /mrange/MiniJson/master/src/MiniJson/MiniJson.fs 3 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/paket.dependencies: -------------------------------------------------------------------------------- 1 | http https://raw.githubusercontent.com/mrange/MiniJson/master/src/MiniJson/MiniJson.fs 2 | http https://raw.githubusercontent.com/mrange/MiniJson/master/src/MiniJson/MiniJson.Dynamic.fs 3 | -------------------------------------------------------------------------------- /src/MiniJson.Paket/paket.lock: -------------------------------------------------------------------------------- 1 | 2 | HTTP 3 | remote: https://raw.githubusercontent.com 4 | specs: 5 | MiniJson.Dynamic.fs (/mrange/MiniJson/master/src/MiniJson/MiniJson.Dynamic.fs) 6 | MiniJson.fs (/mrange/MiniJson/master/src/MiniJson/MiniJson.fs) -------------------------------------------------------------------------------- /src/MiniJson.Reference/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/MiniJson.Reference/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.InteropServices 20 | 21 | [] 22 | [] 23 | do 24 | () -------------------------------------------------------------------------------- /src/MiniJson.Reference/MiniJson.Reference.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 821c50d0-c33e-4ceb-b1c5-767ca682083a 9 | Library 10 | MiniJson.Reference 11 | MiniJson.Reference 12 | v4.5.2 13 | true 14 | 4.4.0.0 15 | MiniJson.Reference 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 5 25 | AnyCPU 26 | bin\Debug\MiniJson.Reference.XML 27 | true 28 | 29 | 30 | pdbonly 31 | true 32 | true 33 | bin\Release\ 34 | TRACE 35 | 5 36 | AnyCPU 37 | bin\Release\MiniJson.Reference.XML 38 | true 39 | 40 | 41 | 11 42 | 43 | 44 | true 45 | full 46 | false 47 | false 48 | bin\Debug\ 49 | DEBUG;TRACE 50 | 5 51 | bin\Debug\MiniJson.Reference.XML 52 | true 53 | x64 54 | 55 | 56 | pdbonly 57 | true 58 | true 59 | bin\Release\ 60 | TRACE 61 | 5 62 | bin\Release\MiniJson.Reference.XML 63 | true 64 | x64 65 | 66 | 67 | true 68 | full 69 | false 70 | false 71 | bin\Debug\ 72 | DEBUG;TRACE 73 | 5 74 | bin\Debug\MiniJson.Reference.XML 75 | true 76 | x86 77 | 78 | 79 | pdbonly 80 | true 81 | true 82 | bin\Release\ 83 | TRACE 84 | 5 85 | bin\Release\MiniJson.Reference.XML 86 | true 87 | x86 88 | 89 | 90 | 91 | 92 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 93 | 94 | 95 | 96 | 97 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 98 | 99 | 100 | 101 | 102 | 103 | 104 | Global_AssemblyInfo.fs 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ..\packages\FParsec.1.0.1\lib\net40-client\FParsec.dll 114 | True 115 | 116 | 117 | ..\packages\FParsec.1.0.1\lib\net40-client\FParsecCS.dll 118 | True 119 | 120 | 121 | 122 | FSharp.Core 123 | FSharp.Core.dll 124 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll 125 | 126 | 127 | 128 | 129 | 130 | MiniJson 131 | {68c7233e-b616-4233-a3b8-6881f11dfb2f} 132 | True 133 | 134 | 135 | 142 | -------------------------------------------------------------------------------- /src/MiniJson.Reference/Reference.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.ReferenceJsonModule 17 | // ---------------------------------------------------------------------------------------------- 18 | open System 19 | open System.Globalization 20 | open System.Text 21 | open System.Threading 22 | // ---------------------------------------------------------------------------------------------- 23 | 24 | // ---------------------------------------------------------------------------------------------- 25 | module Details = 26 | open FParsec.Primitives 27 | open FParsec.CharParsers 28 | 29 | open MiniJson.JsonModule 30 | 31 | type UserState = unit 32 | type Parser<'t> = Parser<'t, UserState> 33 | 34 | let pjson = 35 | let MinimumPow10 = -323 // Min Exponent is -1022 (binary) but since double supports subnormals effective is even lower 36 | 37 | let MaximumPow10 = 308 // 1023 (binary) 38 | 39 | let puint64 = puint64 "digit" 40 | 41 | let parray , rparray = createParserForwardedToRef () 42 | 43 | let pobject , rpobject = createParserForwardedToRef () 44 | 45 | let pnull = stringReturn "null" JsonNull 46 | 47 | let pboolean = stringReturn "true" (JsonBoolean true) <|> stringReturn "false" (JsonBoolean false) 48 | 49 | let prawuintf = 50 | let p = 51 | let fromDigit(ch : char)= float ch - float '0' 52 | let fold f d = 10.*f + fromDigit d 53 | Inline.Many (fromDigit , fold, id, digit) 54 | 55 | pipe3 getPosition p getPosition (fun p ui n -> ui,(n.Index - p.Index)) 56 | 57 | let puintf = prawuintf |>> fun (f, _) -> f 58 | 59 | let pnumber = 60 | let inline pow i = pown 10.0 i 61 | let pminus : Parserfloat>= 62 | charReturn '-' (fun d -> -d) 63 | <|>% id 64 | let psign : Parserfloat>= 65 | charReturn '+' id <|> charReturn '-' (fun d -> -d) 66 | <|>% id 67 | let pfrac = 68 | pipe2 (skipChar '.') prawuintf (fun _ (uf,c) -> uf * (pow (int -c))) 69 | <|>% 0.0 70 | let pexp = 71 | let p = 72 | pipe3 (anyOf "eE") psign puintf (fun _ sign e -> int (sign e)) 73 | |>> fun e -> 74 | if e < MinimumPow10 then 0. 75 | elif e > MaximumPow10 then Double.PositiveInfinity 76 | else pow e 77 | p <|>% 1.0 78 | 79 | let pzero = 80 | charReturn '0' 0.0 81 | pipe4 pminus (pzero <|> puintf) pfrac pexp (fun s i f e -> JsonNumber (s (i + f)*e)) 82 | 83 | let prawstring = 84 | let phex = 85 | hex 86 | |>> fun ch -> 87 | if Char.IsDigit ch then int ch - int '0' 88 | elif ch >= 'A' && ch <= 'F' then int ch - int 'A' + 10 89 | elif ch >= 'a' && ch <= 'f' then int ch - int 'a' + 10 90 | else 0 91 | let pstring_token = skipChar '"' 92 | let pesc_token = skipChar '\\' 93 | let pch = satisfyL (function '"' | '\\' | '\n'| '\r' -> false | _ -> true) "char" 94 | let pech = 95 | let psimple = 96 | anyOf "\"\\/bfnrt" 97 | |>> function 98 | | 'b' -> '\b' 99 | | 'f' -> '\f' 100 | | 'n' -> '\n' 101 | | 'r' -> '\r' 102 | | 't' -> '\t' 103 | | c -> c 104 | let punicode = 105 | pipe5 (skipChar 'u') phex phex phex phex (fun _ v0 v1 v2 v3 -> 106 | let ui = (v0 <<< 12) + (v1 <<< 8) + (v2 <<< 4) + v3 107 | char ui 108 | ) 109 | pesc_token 110 | >>. (psimple <|> punicode) 111 | let pstr = manyChars (choice [pch; pech]) 112 | between pstring_token pstring_token pstr 113 | 114 | let pstring = prawstring |>> JsonString 115 | 116 | let pvalue = choice [pnull; pboolean; pnumber; pstring; parray; pobject] .>> spaces 117 | 118 | let ptk ch = skipChar ch .>> spaces 119 | 120 | let parr = 121 | let pvalues = sepBy pvalue (ptk ',') |>> fun vs -> JsonArray (vs |> List.toArray) 122 | between (ptk '[') (ptk ']') pvalues 123 | 124 | let pobj = 125 | let pmember = 126 | pipe3 (prawstring .>> spaces) (ptk ':') pvalue (fun k _ v -> k,v) 127 | let pmembers = sepBy pmember (ptk ',') |>> fun ms -> JsonObject (ms |> List.toArray) 128 | between (ptk '{') (ptk '}') pmembers 129 | 130 | rparray := parr 131 | rpobject := pobj 132 | 133 | spaces >>. (pobject <|> parray) .>> spaces .>> eof 134 | // ---------------------------------------------------------------------------------------------- 135 | 136 | // ---------------------------------------------------------------------------------------------- 137 | open FParsec 138 | open FParsec.CharParsers 139 | open FParsec.Primitives 140 | // ---------------------------------------------------------------------------------------------- 141 | let parse (input : string) : MiniJson.JsonModule.ParseResult = 142 | match run Details.pjson input with 143 | | Success (v, _, _) -> MiniJson.JsonModule.Success v 144 | | Failure (msg,err,_) -> MiniJson.JsonModule.Failure (msg, int err.Position.Index) 145 | // ---------------------------------------------------------------------------------------------- 146 | -------------------------------------------------------------------------------- /src/MiniJson.Reference/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.InteropServices 20 | 21 | [] 22 | [] 23 | do 24 | () -------------------------------------------------------------------------------- /src/MiniJson.Tests/FSharpData.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.Tests.FSharpData 17 | // ---------------------------------------------------------------------------------------------- 18 | 19 | // ---------------------------------------------------------------------------------------------- 20 | open FSharp.Data 21 | 22 | open MiniJson.JsonModule 23 | // ---------------------------------------------------------------------------------------------- 24 | let rawParse (input : string) : JsonValue = JsonValue.Parse (input) 25 | 26 | let rec convert = function 27 | | JsonValue.String s -> JsonString s 28 | | JsonValue.Number n -> JsonNumber (float n) 29 | | JsonValue.Float f -> JsonNumber f 30 | | JsonValue.Record members -> JsonObject (members |> Array.map (fun (k,v) -> k, convert v)) 31 | | JsonValue.Array values -> JsonArray (values |> Array.map convert) 32 | | JsonValue.Boolean b -> JsonBoolean b 33 | | JsonValue.Null -> JsonNull 34 | 35 | let parse (input : string) : ParseResult = 36 | try 37 | Success <| convert (rawParse input) 38 | with 39 | | ex -> Failure (ex.Message, 0) 40 | 41 | let dummyParse (input : string) : ParseResult = 42 | ignore <| rawParse input 43 | Success <| JsonNull 44 | // ---------------------------------------------------------------------------------------------- 45 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/Jil.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.Tests.Jil 17 | // ---------------------------------------------------------------------------------------------- 18 | 19 | // ---------------------------------------------------------------------------------------------- 20 | open System.Collections.Generic 21 | 22 | open Jil 23 | 24 | open MiniJson.JsonModule 25 | // ---------------------------------------------------------------------------------------------- 26 | let rawParse (input : string) : obj = JSON.Deserialize input 27 | 28 | (* 29 | let rec convert = function 30 | | JsonValue.String s -> JsonString s 31 | | JsonValue.Number n -> JsonNumber (float n) 32 | | JsonValue.Float f -> JsonNumber f 33 | | JsonValue.Record members -> JsonObject (members |> Array.map (fun (k,v) -> k, convert v)) 34 | | JsonValue.Array values -> JsonArray (values |> Array.map convert) 35 | | JsonValue.Boolean b -> JsonBoolean b 36 | | JsonValue.Null -> JsonNull 37 | *) 38 | 39 | let parse (input : string) : ParseResult = 40 | try 41 | // TODO: Convert to AST 42 | let x = rawParse input 43 | Success <| JsonNull 44 | with 45 | | ex -> Failure (ex.Message, 0) 46 | 47 | let dummyParse (input : string) : ParseResult = 48 | ignore <| rawParse input 49 | Success <| JsonNull 50 | // ---------------------------------------------------------------------------------------------- 51 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/JsonNet.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.Tests.JsonNet 17 | // ---------------------------------------------------------------------------------------------- 18 | 19 | // ---------------------------------------------------------------------------------------------- 20 | open System 21 | open System.Collections.Generic 22 | open System.Diagnostics 23 | open System.IO 24 | 25 | open Newtonsoft.Json 26 | 27 | open MiniJson.JsonModule 28 | // ---------------------------------------------------------------------------------------------- 29 | 30 | // ---------------------------------------------------------------------------------------------- 31 | module Details = 32 | [] 33 | [] 34 | type JsonBuilder = 35 | | BuilderRoot of Json ref 36 | | BuilderObject of string ref*ResizeArray 37 | | BuilderArray of ResizeArray 38 | 39 | open Details 40 | 41 | let parse (input : string) : ParseResult = 42 | use ss = new StringReader(input) 43 | use jtr = new JsonTextReader(ss) 44 | 45 | try 46 | 47 | let context = Stack () 48 | context.Push <| (BuilderRoot <| ref JsonNull) 49 | 50 | let push v = 51 | context.Push v 52 | true 53 | 54 | let pop () = 55 | match context.Pop () with 56 | | BuilderRoot vr -> !vr 57 | | BuilderObject (_,ms) -> JsonObject (ms.ToArray ()) 58 | | BuilderArray vs -> JsonArray (vs.ToArray ()) 59 | 60 | let setKey k = 61 | match context.Peek () with 62 | | BuilderRoot vr -> () 63 | | BuilderObject (rk,_) -> rk := k 64 | | BuilderArray vs -> () 65 | true 66 | 67 | let add v = 68 | match context.Peek () with 69 | | BuilderRoot vr -> vr := v 70 | | BuilderObject (rk,ms) -> ms.Add (!rk, v) 71 | | BuilderArray vs -> vs.Add v 72 | true 73 | 74 | let defaultSize = 4 75 | 76 | let inline str () = Convert.ToString jtr.Value 77 | let inline number () = Convert.ToDouble jtr.Value 78 | let inline boolean () = Convert.ToBoolean jtr.Value 79 | 80 | let rec loop () = 81 | if jtr.Read () then 82 | let result = 83 | match jtr.TokenType with 84 | | JsonToken.Boolean -> add <| (JsonBoolean <| boolean ()) 85 | | JsonToken.Bytes -> false 86 | | JsonToken.Comment -> false 87 | | JsonToken.Date -> false 88 | | JsonToken.EndArray -> add <| pop () 89 | | JsonToken.EndConstructor -> false 90 | | JsonToken.EndObject -> add <| pop () 91 | | JsonToken.Float -> add <| (JsonNumber <| number ()) 92 | | JsonToken.Integer -> add <| (JsonNumber <| number ()) 93 | | JsonToken.None -> false 94 | | JsonToken.Null -> add <| JsonNull 95 | | JsonToken.PropertyName -> setKey <| str () 96 | | JsonToken.Raw -> false 97 | | JsonToken.StartArray -> push (BuilderArray <| ResizeArray<_>(defaultSize)) 98 | | JsonToken.StartConstructor -> false 99 | | JsonToken.StartObject -> push (BuilderObject <| (ref "", ResizeArray<_>(defaultSize))) 100 | | JsonToken.String -> add <| (JsonString <| str ()) 101 | | JsonToken.Undefined 102 | | _ -> false 103 | result && loop () 104 | else 105 | true 106 | 107 | match loop () with 108 | | true -> 109 | Debug.Assert (context.Count = 1) 110 | match context.Pop () with 111 | | BuilderRoot root -> Success !root 112 | | _ -> 113 | Debug.Assert false 114 | Failure ("Json buildup failure", -1) 115 | | false -> 116 | Failure ("Parse failure", jtr.LinePosition) 117 | with 118 | | ex -> Failure ("Parse failure: " + ex.Message, jtr.LinePosition + jtr.LineNumber*1000) 119 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/MiniJson.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 387b94cc-4e4d-4f3c-bf80-5916c895e404 9 | Exe 10 | MiniJson.Tests 11 | MiniJson.Tests 12 | v4.5.2 13 | true 14 | 4.4.0.0 15 | MiniJson.Tests 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 5 25 | AnyCPU 26 | bin\Debug\MiniJson.Tests.XML 27 | false 28 | 29 | 30 | pdbonly 31 | true 32 | true 33 | bin\Release\ 34 | TRACE 35 | 5 36 | AnyCPU 37 | bin\Release\MiniJson.Tests.XML 38 | false 39 | 40 | 41 | 11 42 | 43 | 44 | true 45 | full 46 | false 47 | false 48 | bin\Debug\ 49 | DEBUG;TRACE 50 | 5 51 | bin\Debug\MiniJson.Tests.XML 52 | true 53 | x64 54 | 55 | 56 | pdbonly 57 | true 58 | true 59 | bin\Release\ 60 | TRACE 61 | 5 62 | bin\Release\MiniJson.Tests.XML 63 | true 64 | x64 65 | 66 | 67 | true 68 | full 69 | false 70 | false 71 | bin\Debug\ 72 | DEBUG;TRACE 73 | 5 74 | bin\Debug\MiniJson.Tests.XML 75 | true 76 | x86 77 | 78 | 79 | pdbonly 80 | true 81 | true 82 | bin\Release\ 83 | TRACE 84 | 5 85 | bin\Release\MiniJson.Tests.XML 86 | true 87 | x86 88 | 89 | 90 | 91 | 92 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 93 | 94 | 95 | 96 | 97 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 98 | 99 | 100 | 101 | 102 | 103 | 104 | Global_AssemblyInfo.fs 105 | 106 | 107 | 108 | PreserveNewest 109 | 110 | 111 | PreserveNewest 112 | 113 | 114 | PreserveNewest 115 | 116 | 117 | PreserveNewest 118 | 119 | 120 | PreserveNewest 121 | 122 | 123 | PreserveNewest 124 | 125 | 126 | PreserveNewest 127 | 128 | 129 | PreserveNewest 130 | 131 | 132 | PreserveNewest 133 | 134 | 135 | PreserveNewest 136 | 137 | 138 | PreserveNewest 139 | 140 | 141 | PreserveNewest 142 | 143 | 144 | PreserveNewest 145 | 146 | 147 | PreserveNewest 148 | 149 | 150 | PreserveNewest 151 | 152 | 153 | PreserveNewest 154 | 155 | 156 | PreserveNewest 157 | 158 | 159 | PreserveNewest 160 | 161 | 162 | PreserveNewest 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | ..\packages\FSharp.Data.2.2.5\lib\net40\FSharp.Data.dll 176 | True 177 | 178 | 179 | ..\packages\Jil.2.12.1\lib\net45\Jil.dll 180 | True 181 | 182 | 183 | 184 | FSharp.Core 185 | FSharp.Core.dll 186 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll 187 | 188 | 189 | ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll 190 | True 191 | 192 | 193 | ..\packages\Sigil.4.5.0\lib\net45\Sigil.dll 194 | True 195 | 196 | 197 | 198 | 199 | 200 | MiniJson.Reference 201 | {821c50d0-c33e-4ceb-b1c5-767ca682083a} 202 | True 203 | 204 | 205 | MiniJson 206 | {68c7233e-b616-4233-a3b8-6881f11dfb2f} 207 | True 208 | 209 | 210 | 211 | 212 | 219 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/Test.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.Tests.Test 17 | // ---------------------------------------------------------------------------------------------- 18 | 19 | // ---------------------------------------------------------------------------------------------- 20 | open System 21 | open Microsoft.FSharp.Core.Printf 22 | // ---------------------------------------------------------------------------------------------- 23 | 24 | // ---------------------------------------------------------------------------------------------- 25 | let runningOnMono = Type.GetType ("Mono.Runtime") <> null 26 | let linuxLineEnding = Environment.NewLine = "\n" 27 | 28 | let mutable errors = 0 29 | 30 | let print (cc : ConsoleColor) (prelude : string) (msg : string) : unit = 31 | let p = Console.ForegroundColor 32 | try 33 | Console.ForegroundColor <- cc 34 | Console.Write prelude 35 | Console.WriteLine msg 36 | finally 37 | Console.ForegroundColor <- p 38 | 39 | let error msg = 40 | errors <- errors + 1 41 | print ConsoleColor.Red "ERROR : " msg 42 | 43 | let warning msg = print ConsoleColor.Yellow "WARNING: " msg 44 | let info msg = print ConsoleColor.Gray "INFO : " msg 45 | let success msg = print ConsoleColor.Green "SUCCESS: " msg 46 | let highlight msg = print ConsoleColor.White "HILIGHT: " msg 47 | 48 | let errorf f = kprintf error f 49 | let warningf f = kprintf warning f 50 | let infof f = kprintf info f 51 | let successf f = kprintf success f 52 | let highlightf f = kprintf highlight f 53 | 54 | let test_failure msg = errorf "TEST: %s" msg 55 | let test_failuref f = kprintf test_failure f 56 | 57 | let test_eq e a nm = 58 | if e = a then true 59 | else 60 | errorf "TEST_EQ: %A = %A (%s)" e a nm 61 | false 62 | 63 | let test_gt e a nm = 64 | if e > a then true 65 | else 66 | errorf "TEST_GT: %A > %A (%s)" e a nm 67 | false 68 | 69 | let test_lt e a nm = 70 | if e < a then true 71 | else 72 | errorf "TEST_LT: %A < %A (%s)" e a nm 73 | false 74 | 75 | let check_eq e a nm = ignore <| test_eq e a nm 76 | let check_gt e a nm = ignore <| test_gt e a nm 77 | let check_lt e a nm = ignore <| test_lt e a nm 78 | 79 | 80 | // ---------------------------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | module MiniJson.Tests.TestCases 17 | // ---------------------------------------------------------------------------------------------- 18 | 19 | // ---------------------------------------------------------------------------------------------- 20 | open System 21 | open System.IO 22 | 23 | open MiniJson.Tests.Test 24 | // ---------------------------------------------------------------------------------------------- 25 | 26 | // ---------------------------------------------------------------------------------------------- 27 | let positiveTestCases = 28 | [| 29 | """[0]""" 30 | """[-0]""" 31 | """[0.125]""" 32 | """[-0.125]""" 33 | """[0e2]""" 34 | """[-0E2]""" 35 | """[0.125E3]""" 36 | """[-0.125E2]""" 37 | """[125]""" 38 | """[-125]""" 39 | """[1.25]""" 40 | """[-12.5]""" 41 | """[1.25E2]""" 42 | """[-12.5E-2]""" 43 | """[-12.5e+2]""" 44 | """[null]""" 45 | """[true]""" 46 | """[false]""" 47 | """["Hello\r\nThere"]""" 48 | """["Hello\u004a"]""" 49 | """["\"\\\/\b\f\n\r\t\u2665"]""" 50 | """["\u0123\u4567\u89AB\uCDEF"]""" 51 | """["\u0123\u4567\u89ab\ucdef"]""" 52 | """[false,true,null]""" 53 | """[ false ,true, null ]""" 54 | """[[], null, [true]]""" 55 | """{"abc":123}""" 56 | """{"abc" :123}""" 57 | """{ "abc":123}""" 58 | """{ "abc" :123}""" 59 | "\t[\rfalse \r\n, true\n]\t" 60 | "[\"" + String.Join ("", Array.init 32 id |> Array.map (fun v -> sprintf "\u%04X" v)) + "\"]" 61 | """[1E-324]""" 62 | """[1E+309]""" 63 | """[-1E309]""" 64 | |] |> Array.map (fun v -> true, sprintf "Positive: %s" v, v) 65 | 66 | let noIndentOracles = 67 | [| 68 | """[0]""" 69 | """[0]""" 70 | """[0.125]""" 71 | """[-0.125]""" 72 | """[0]""" 73 | """[0]""" 74 | """[125]""" 75 | """[-12.5]""" 76 | """[125]""" 77 | """[-125]""" 78 | """[1.25]""" 79 | """[-12.5]""" 80 | """[125]""" 81 | """[-0.125]""" 82 | """[-1250]""" 83 | """[null]""" 84 | """[true]""" 85 | """[false]""" 86 | """["Hello\r\nThere"]""" 87 | """["HelloJ"]""" 88 | """["\"\\\/\b\f\n\r\t♥"]""" 89 | "[\"\u0123\u4567\u89AB\uCDEF\"]" 90 | "[\"\u0123\u4567\u89ab\ucdef\"]" 91 | """[false,true,null]""" 92 | """[false,true,null]""" 93 | """[[],null,[true]]""" 94 | """{"abc":123}""" 95 | """{"abc":123}""" 96 | """{"abc":123}""" 97 | """{"abc":123}""" 98 | "[false,true]" 99 | """["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"]""" 100 | """[0]""" 101 | """["+Inf"]""" 102 | """["-Inf"]""" 103 | |] 104 | 105 | let withIndentOracles = 106 | [| 107 | """[ 108 | 0 109 | ]""" 110 | 111 | """[ 112 | 0 113 | ]""" 114 | 115 | """[ 116 | 0.125 117 | ]""" 118 | 119 | """[ 120 | -0.125 121 | ]""" 122 | 123 | """[ 124 | 0 125 | ]""" 126 | 127 | """[ 128 | 0 129 | ]""" 130 | 131 | """[ 132 | 125 133 | ]""" 134 | 135 | """[ 136 | -12.5 137 | ]""" 138 | 139 | """[ 140 | 125 141 | ]""" 142 | 143 | """[ 144 | -125 145 | ]""" 146 | 147 | """[ 148 | 1.25 149 | ]""" 150 | 151 | """[ 152 | -12.5 153 | ]""" 154 | 155 | """[ 156 | 125 157 | ]""" 158 | 159 | """[ 160 | -0.125 161 | ]""" 162 | 163 | """[ 164 | -1250 165 | ]""" 166 | 167 | """[ 168 | null 169 | ]""" 170 | 171 | """[ 172 | true 173 | ]""" 174 | 175 | """[ 176 | false 177 | ]""" 178 | 179 | """[ 180 | "Hello\r\nThere" 181 | ]""" 182 | 183 | """[ 184 | "HelloJ" 185 | ]""" 186 | 187 | """[ 188 | "\"\\\/\b\f\n\r\t♥" 189 | ]""" 190 | 191 | "[\r\n \"\u0123\u4567\u89AB\uCDEF\"\r\n]" 192 | "[\r\n \"\u0123\u4567\u89ab\ucdef\"\r\n]" 193 | 194 | """[ 195 | false, 196 | true, 197 | null 198 | ]""" 199 | 200 | """[ 201 | false, 202 | true, 203 | null 204 | ]""" 205 | 206 | """[ 207 | [ 208 | ], 209 | null, 210 | [ 211 | true 212 | ] 213 | ]""" 214 | 215 | """{ 216 | "abc": 217 | 123 218 | }""" 219 | 220 | """{ 221 | "abc": 222 | 123 223 | }""" 224 | 225 | """{ 226 | "abc": 227 | 123 228 | }""" 229 | 230 | """{ 231 | "abc": 232 | 123 233 | }""" 234 | 235 | """[ 236 | false, 237 | true 238 | ]""" 239 | 240 | """[ 241 | "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F" 242 | ]""" 243 | 244 | """[ 245 | 0 246 | ]""" 247 | 248 | """[ 249 | "+Inf" 250 | ]""" 251 | 252 | """[ 253 | "-Inf" 254 | ]""" 255 | |] 256 | 257 | let negativeTestCases = 258 | [| 259 | """null""" 260 | """true""" 261 | """false""" 262 | """0""" 263 | """-0""" 264 | """125""" 265 | """-125""" 266 | "\"Hello\"" 267 | """[01]""" 268 | """[-01]""" 269 | """[0125]""" 270 | """[+0]""" 271 | """[+125]""" 272 | """[.1]""" 273 | """[-.0]""" 274 | """[1.]""" 275 | """[1E]""" 276 | """[1E+]""" 277 | """[1E-]""" 278 | """["Hello]""" 279 | """["Hello\xThere"]""" 280 | """["Hello\u"]""" 281 | """["Hello\u00"]""" 282 | """["Hello\uPQ"]""" 283 | """{abc:3}""" 284 | """{"abc:3}""" 285 | """{"abc":}""" 286 | """["Hello this is a somewhat wide errornous string, to demonstrate \ERROR window"]""" 287 | """["Hello this is a wide errornous string, to demonstrate error window capabilities... The window is around 60 chars so now is the time for \ERROR"]""" 288 | """["Hello this is a wide errornous string, to demonstrate error window capabilities, here is the \ERROR... The window is around 60 chars"]""" 289 | """["Early \ERROR, hello this is a wide errornous string, to demonstrate error window capabilities. The window is around 60 chars"]""" 290 | """[fals""" 291 | """[t""" 292 | """["Hello\u""" 293 | """""" 294 | """[] []""" 295 | """[" 296 | "]""" 297 | """[-""" 298 | """[1.0""" 299 | """[nul]""" 300 | |] |> Array.map (fun v -> false, sprintf "Negative: %s" v, v) 301 | 302 | let errorReportingOracles = 303 | [| 304 | """Failed to parse input as JSON 305 | null 306 | ^ Pos: 0 307 | Expected: '[' or '{'""" 308 | 309 | """Failed to parse input as JSON 310 | true 311 | ^ Pos: 0 312 | Expected: '[' or '{'""" 313 | 314 | """Failed to parse input as JSON 315 | false 316 | ^ Pos: 0 317 | Expected: '[' or '{'""" 318 | 319 | """Failed to parse input as JSON 320 | 0 321 | ^ Pos: 0 322 | Expected: '[' or '{'""" 323 | 324 | """Failed to parse input as JSON 325 | -0 326 | ^ Pos: 0 327 | Expected: '[' or '{'""" 328 | 329 | """Failed to parse input as JSON 330 | 125 331 | ^ Pos: 0 332 | Expected: '[' or '{'""" 333 | 334 | """Failed to parse input as JSON 335 | -125 336 | ^ Pos: 0 337 | Expected: '[' or '{'""" 338 | 339 | """Failed to parse input as JSON 340 | "Hello" 341 | ^ Pos: 0 342 | Expected: '[' or '{'""" 343 | 344 | """Failed to parse input as JSON 345 | [01] 346 | --^ Pos: 2 347 | Expected: ',', '.', 'E' or 'e'""" 348 | 349 | """Failed to parse input as JSON 350 | [-01] 351 | ---^ Pos: 3 352 | Expected: ',', '.', 'E' or 'e'""" 353 | 354 | """Failed to parse input as JSON 355 | [0125] 356 | --^ Pos: 2 357 | Expected: ',', '.', 'E' or 'e'""" 358 | 359 | """Failed to parse input as JSON 360 | [+0] 361 | -^ Pos: 1 362 | Expected: '"', '-', '[', '{', digit, false, null or true""" 363 | 364 | """Failed to parse input as JSON 365 | [+125] 366 | -^ Pos: 1 367 | Expected: '"', '-', '[', '{', digit, false, null or true""" 368 | 369 | """Failed to parse input as JSON 370 | [.1] 371 | -^ Pos: 1 372 | Expected: '"', '-', '[', '{', digit, false, null or true""" 373 | 374 | """Failed to parse input as JSON 375 | [-.0] 376 | --^ Pos: 2 377 | Expected: '0' or digit""" // TODO: Improve this error message by removing '0' 378 | 379 | """Failed to parse input as JSON 380 | [1.] 381 | ---^ Pos: 3 382 | Expected: digit""" 383 | 384 | """Failed to parse input as JSON 385 | [1E] 386 | ---^ Pos: 3 387 | Expected: '+', '-' or digit""" 388 | 389 | """Failed to parse input as JSON 390 | [1E+] 391 | ----^ Pos: 4 392 | Expected: digit""" 393 | 394 | """Failed to parse input as JSON 395 | [1E-] 396 | ----^ Pos: 4 397 | Expected: digit""" 398 | 399 | """Failed to parse input as JSON 400 | ["Hello] 401 | --------^ Pos: 8 402 | Expected: char 403 | Unexpected: EOS""" 404 | 405 | """Failed to parse input as JSON 406 | ["Hello\xThere"] 407 | --------^ Pos: 8 408 | Expected: '"', '/', '\', 'b', 'f', 'n', 'r', 't' or 'u'""" 409 | 410 | """Failed to parse input as JSON 411 | ["Hello\u"] 412 | ---------^ Pos: 9 413 | Expected: hexdigit""" 414 | 415 | """Failed to parse input as JSON 416 | ["Hello\u00"] 417 | -----------^ Pos: 11 418 | Expected: hexdigit""" 419 | 420 | """Failed to parse input as JSON 421 | ["Hello\uPQ"] 422 | ---------^ Pos: 9 423 | Expected: hexdigit""" 424 | 425 | """Failed to parse input as JSON 426 | {abc:3} 427 | -^ Pos: 1 428 | Expected: '"'""" 429 | 430 | """Failed to parse input as JSON 431 | {"abc:3} 432 | --------^ Pos: 8 433 | Expected: char 434 | Unexpected: EOS""" 435 | 436 | """Failed to parse input as JSON 437 | {"abc":} 438 | -------^ Pos: 7 439 | Expected: '"', '-', '[', '{', digit, false, null or true""" 440 | 441 | """Failed to parse input as JSON 442 | rnous string, to demonstrate \ERROR window"] 443 | ------------------------------^ Pos: 66 444 | Expected: '"', '/', '\', 'b', 'f', 'n', 'r', 't' or 'u'""" 445 | 446 | """Failed to parse input as JSON 447 | chars so now is the time for \ERROR"] 448 | ------------------------------^ Pos: 139 449 | Expected: '"', '/', '\', 'b', 'f', 'n', 'r', 't' or 'u'""" 450 | 451 | """Failed to parse input as JSON 452 | ow capabilities, here is the \ERROR... The window is around 6 453 | ------------------------------^ Pos: 96 454 | Expected: '"', '/', '\', 'b', 'f', 'n', 'r', 't' or 'u'""" 455 | 456 | """Failed to parse input as JSON 457 | ["Early \ERROR, hello this is a wide errornous string, to dem 458 | ---------^ Pos: 9 459 | Expected: '"', '/', '\', 'b', 'f', 'n', 'r', 't' or 'u'""" 460 | """Failed to parse input as JSON 461 | [fals 462 | -^ Pos: 1 463 | Expected: '"', '-', '[', '{', digit, false, null or true""" 464 | """Failed to parse input as JSON 465 | [t 466 | -^ Pos: 1 467 | Expected: '"', '-', '[', '{', digit, false, null or true""" 468 | 469 | """Failed to parse input as JSON 470 | ["Hello\u 471 | ---------^ Pos: 9 472 | Expected: hexdigit 473 | Unexpected: EOS""" 474 | 475 | """Failed to parse input as JSON 476 | 477 | ^ Pos: 0 478 | Expected: '[' or '{' 479 | Unexpected: EOS""" 480 | 481 | """Failed to parse input as JSON 482 | [] [] 483 | ---^ Pos: 3 484 | Expected: EOS""" 485 | 486 | """Failed to parse input as JSON 487 | [" "] 488 | --^ Pos: 2 489 | Unexpected: NEWLINE""" 490 | 491 | """Failed to parse input as JSON 492 | [- 493 | --^ Pos: 2 494 | Expected: '0' or digit 495 | Unexpected: EOS""" 496 | 497 | """Failed to parse input as JSON 498 | [1.0 499 | ----^ Pos: 4 500 | Expected: ',', 'E', 'e' or digit 501 | Unexpected: EOS""" 502 | 503 | """Failed to parse input as JSON 504 | [nul] 505 | -^ Pos: 1 506 | Expected: '"', '-', '[', '{', digit, false, null or true""" 507 | |] 508 | 509 | 510 | let sampleTestCases = 511 | try 512 | let path = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "TestCases") 513 | let jsons = Directory.GetFiles (path, "*.json") 514 | 515 | jsons 516 | |> Array.map (fun json -> true, sprintf "Sample: %s" <| Path.GetFileName json, File.ReadAllText json) 517 | with 518 | | ex -> errorf "EXCEPTION: %s" ex.Message; [||] 519 | 520 | // ---------------------------------------------------------------------------------------------- 521 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/Dates.json: -------------------------------------------------------------------------------- 1 | { 2 | "birthdate": "\/Date(869080830450)\/", 3 | "anniversary": "1997-07-16T19:20:30.45+01:00", 4 | "NoTimeZone": "1997-07-16T19:20:30", 5 | "UtcTime": "1997-07-16T19:50:30Z" 6 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/DoubleNested.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "title": "example", 4 | "nested": { "nestedTitle": "sub" } 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/Empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/Nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "firstName": "John", 4 | "lastName": "Doe", 5 | "age": 25, 6 | "isCool": true 7 | } 8 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/OptionValues.json: -------------------------------------------------------------------------------- 1 | { "authors": [{ "name": "Steffen", "age": 29 }, { "name": "Tomas" }]} -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/Simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "firstName": "John", 3 | "lastName": "Doe", 4 | "age": 25, 5 | "isCool": true 6 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/SimpleArray.json: -------------------------------------------------------------------------------- 1 | { "items": [{ "id": "Open"}, {"id": "Pause"}]} -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/TypeInference.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "intLike" : "123" , 4 | "boolLike1" : 0 , 5 | "boolLike2" : "1" 6 | }, 7 | { 8 | "intLike" : "321" , 9 | "boolLike1" : 1 , 10 | "boolLike2" : "0" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/Vindinium.json: -------------------------------------------------------------------------------- 1 | { 2 | "game":{ 3 | "id":"s2xh3aig", 4 | "turn":1100, 5 | "maxTurns":1200, 6 | "heroes":[ 7 | { 8 | "id":1, 9 | "name":"vjousse", 10 | "userId":"j07ws669", 11 | "elo":1200, 12 | "pos":{ 13 | "x":5, 14 | "y":6 15 | }, 16 | "life":60, 17 | "gold":0, 18 | "mineCount":0, 19 | "spawnPos":{ 20 | "x":5, 21 | "y":6 22 | }, 23 | "crashed":true 24 | }, 25 | { 26 | "id":2, 27 | "name":"vjousse", 28 | "userId":"j07ws669", 29 | "elo":1200, 30 | "pos":{ 31 | "x":12, 32 | "y":6 33 | }, 34 | "life":100, 35 | "gold":0, 36 | "mineCount":0, 37 | "spawnPos":{ 38 | "x":12, 39 | "y":6 40 | }, 41 | "crashed":true 42 | }, 43 | { 44 | "id":3, 45 | "name":"vjousse", 46 | "userId":"j07ws669", 47 | "elo":1200, 48 | "pos":{ 49 | "x":12, 50 | "y":11 51 | }, 52 | "life":80, 53 | "gold":0, 54 | "mineCount":0, 55 | "spawnPos":{ 56 | "x":12, 57 | "y":11 58 | }, 59 | "crashed":true 60 | }, 61 | { 62 | "id":4, 63 | "name":"vjousse", 64 | "userId":"j07ws669", 65 | "elo":1200, 66 | "pos":{ 67 | "x":4, 68 | "y":8 69 | }, 70 | "life":38, 71 | "gold":1078, 72 | "mineCount":6, 73 | "spawnPos":{ 74 | "x":5, 75 | "y":11 76 | }, 77 | "crashed":false 78 | } 79 | ], 80 | "board":{ 81 | "size":18, 82 | "tiles":"############## ############################ ############################## ##############################$4 $4############################ @4 ######################## @1## ## #################### [] [] ################## #### #################### $4####$4 ######################## $4####$4 #################### #### ################## [] [] #################### @2## ##@3 ######################## ############################$- $-############################## ############################## ############################ ##############" 83 | }, 84 | "finished":true 85 | }, 86 | "hero":{ 87 | "id":4, 88 | "name":"vjousse", 89 | "userId":"j07ws669", 90 | "elo":1200, 91 | "pos":{ 92 | "x":4, 93 | "y":8 94 | }, 95 | "life":38, 96 | "gold":1078, 97 | "mineCount":6, 98 | "spawnPos":{ 99 | "x":5, 100 | "y":11 101 | }, 102 | "crashed":false 103 | }, 104 | "token":"lte0", 105 | "viewUrl":"http://localhost:9000/s2xh3aig", 106 | "playUrl":"http://localhost:9000/api/s2xh3aig/lte0/play" 107 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/WikiData.json: -------------------------------------------------------------------------------- 1 | { 2 | "firstName": "John", 3 | "lastName" : "Doe", 4 | "age" : 42, 5 | "address" : 6 | { 7 | "streetAddress": "21 2nd Street", 8 | "city" : "New York", 9 | "state" : "NY", 10 | "postalCode" : "10021" 11 | } 12 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/WorldBank.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "page": 1, 4 | "pages": 1, 5 | "per_page": "1000", 6 | "total": 53 7 | }, 8 | [ 9 | { 10 | "indicator": { 11 | "id": "GC.DOD.TOTL.GD.ZS", 12 | "value": "Central government debt, total (% of GDP)" 13 | }, 14 | "country": { 15 | "id": "CZ", 16 | "value": "Czech Republic" 17 | }, 18 | "value": null, 19 | "decimal": "1", 20 | "date": "2012" 21 | }, 22 | { 23 | "indicator": { 24 | "id": "GC.DOD.TOTL.GD.ZS", 25 | "value": "Central government debt, total (% of GDP)" 26 | }, 27 | "country": { 28 | "id": "CZ", 29 | "value": "Czech Republic" 30 | }, 31 | "value": null, 32 | "decimal": "1", 33 | "date": "2011" 34 | }, 35 | { 36 | "indicator": { 37 | "id": "GC.DOD.TOTL.GD.ZS", 38 | "value": "Central government debt, total (% of GDP)" 39 | }, 40 | "country": { 41 | "id": "CZ", 42 | "value": "Czech Republic" 43 | }, 44 | "value": "35.1422970266502", 45 | "decimal": "1", 46 | "date": "2010" 47 | }, 48 | { 49 | "indicator": { 50 | "id": "GC.DOD.TOTL.GD.ZS", 51 | "value": "Central government debt, total (% of GDP)" 52 | }, 53 | "country": { 54 | "id": "CZ", 55 | "value": "Czech Republic" 56 | }, 57 | "value": "31.034880222506", 58 | "decimal": "1", 59 | "date": "2009" 60 | }, 61 | { 62 | "indicator": { 63 | "id": "GC.DOD.TOTL.GD.ZS", 64 | "value": "Central government debt, total (% of GDP)" 65 | }, 66 | "country": { 67 | "id": "CZ", 68 | "value": "Czech Republic" 69 | }, 70 | "value": "25.475163645463", 71 | "decimal": "1", 72 | "date": "2008" 73 | }, 74 | { 75 | "indicator": { 76 | "id": "GC.DOD.TOTL.GD.ZS", 77 | "value": "Central government debt, total (% of GDP)" 78 | }, 79 | "country": { 80 | "id": "CZ", 81 | "value": "Czech Republic" 82 | }, 83 | "value": "24.1933198328061", 84 | "decimal": "1", 85 | "date": "2007" 86 | }, 87 | { 88 | "indicator": { 89 | "id": "GC.DOD.TOTL.GD.ZS", 90 | "value": "Central government debt, total (% of GDP)" 91 | }, 92 | "country": { 93 | "id": "CZ", 94 | "value": "Czech Republic" 95 | }, 96 | "value": "23.7080545570765", 97 | "decimal": "1", 98 | "date": "2006" 99 | }, 100 | { 101 | "indicator": { 102 | "id": "GC.DOD.TOTL.GD.ZS", 103 | "value": "Central government debt, total (% of GDP)" 104 | }, 105 | "country": { 106 | "id": "CZ", 107 | "value": "Czech Republic" 108 | }, 109 | "value": "22.0334615295746", 110 | "decimal": "1", 111 | "date": "2005" 112 | }, 113 | { 114 | "indicator": { 115 | "id": "GC.DOD.TOTL.GD.ZS", 116 | "value": "Central government debt, total (% of GDP)" 117 | }, 118 | "country": { 119 | "id": "CZ", 120 | "value": "Czech Republic" 121 | }, 122 | "value": "20.1083787500358", 123 | "decimal": "1", 124 | "date": "2004" 125 | }, 126 | { 127 | "indicator": { 128 | "id": "GC.DOD.TOTL.GD.ZS", 129 | "value": "Central government debt, total (% of GDP)" 130 | }, 131 | "country": { 132 | "id": "CZ", 133 | "value": "Czech Republic" 134 | }, 135 | "value": "18.2677252058791", 136 | "decimal": "1", 137 | "date": "2003" 138 | }, 139 | { 140 | "indicator": { 141 | "id": "GC.DOD.TOTL.GD.ZS", 142 | "value": "Central government debt, total (% of GDP)" 143 | }, 144 | "country": { 145 | "id": "CZ", 146 | "value": "Czech Republic" 147 | }, 148 | "value": "15.4255646477354", 149 | "decimal": "1", 150 | "date": "2002" 151 | }, 152 | { 153 | "indicator": { 154 | "id": "GC.DOD.TOTL.GD.ZS", 155 | "value": "Central government debt, total (% of GDP)" 156 | }, 157 | "country": { 158 | "id": "CZ", 159 | "value": "Czech Republic" 160 | }, 161 | "value": "14.8744342075761", 162 | "decimal": "1", 163 | "date": "2001" 164 | }, 165 | { 166 | "indicator": { 167 | "id": "GC.DOD.TOTL.GD.ZS", 168 | "value": "Central government debt, total (% of GDP)" 169 | }, 170 | "country": { 171 | "id": "CZ", 172 | "value": "Czech Republic" 173 | }, 174 | "value": "13.2188686145055", 175 | "decimal": "1", 176 | "date": "2000" 177 | }, 178 | { 179 | "indicator": { 180 | "id": "GC.DOD.TOTL.GD.ZS", 181 | "value": "Central government debt, total (% of GDP)" 182 | }, 183 | "country": { 184 | "id": "CZ", 185 | "value": "Czech Republic" 186 | }, 187 | "value": "11.3566955774787", 188 | "decimal": "1", 189 | "date": "1999" 190 | }, 191 | { 192 | "indicator": { 193 | "id": "GC.DOD.TOTL.GD.ZS", 194 | "value": "Central government debt, total (% of GDP)" 195 | }, 196 | "country": { 197 | "id": "CZ", 198 | "value": "Czech Republic" 199 | }, 200 | "value": "10.1787800927734", 201 | "decimal": "1", 202 | "date": "1998" 203 | }, 204 | { 205 | "indicator": { 206 | "id": "GC.DOD.TOTL.GD.ZS", 207 | "value": "Central government debt, total (% of GDP)" 208 | }, 209 | "country": { 210 | "id": "CZ", 211 | "value": "Czech Republic" 212 | }, 213 | "value": "10.1535658732129", 214 | "decimal": "1", 215 | "date": "1997" 216 | }, 217 | { 218 | "indicator": { 219 | "id": "GC.DOD.TOTL.GD.ZS", 220 | "value": "Central government debt, total (% of GDP)" 221 | }, 222 | "country": { 223 | "id": "CZ", 224 | "value": "Czech Republic" 225 | }, 226 | "value": "10.5203014347956", 227 | "decimal": "1", 228 | "date": "1996" 229 | }, 230 | { 231 | "indicator": { 232 | "id": "GC.DOD.TOTL.GD.ZS", 233 | "value": "Central government debt, total (% of GDP)" 234 | }, 235 | "country": { 236 | "id": "CZ", 237 | "value": "Czech Republic" 238 | }, 239 | "value": "12.7078339884043", 240 | "decimal": "1", 241 | "date": "1995" 242 | }, 243 | { 244 | "indicator": { 245 | "id": "GC.DOD.TOTL.GD.ZS", 246 | "value": "Central government debt, total (% of GDP)" 247 | }, 248 | "country": { 249 | "id": "CZ", 250 | "value": "Czech Republic" 251 | }, 252 | "value": "14.7818078232509", 253 | "decimal": "1", 254 | "date": "1994" 255 | }, 256 | { 257 | "indicator": { 258 | "id": "GC.DOD.TOTL.GD.ZS", 259 | "value": "Central government debt, total (% of GDP)" 260 | }, 261 | "country": { 262 | "id": "CZ", 263 | "value": "Czech Republic" 264 | }, 265 | "value": "16.6567773464055", 266 | "decimal": "1", 267 | "date": "1993" 268 | }, 269 | { 270 | "indicator": { 271 | "id": "GC.DOD.TOTL.GD.ZS", 272 | "value": "Central government debt, total (% of GDP)" 273 | }, 274 | "country": { 275 | "id": "CZ", 276 | "value": "Czech Republic" 277 | }, 278 | "value": null, 279 | "decimal": "1", 280 | "date": "1992" 281 | }, 282 | { 283 | "indicator": { 284 | "id": "GC.DOD.TOTL.GD.ZS", 285 | "value": "Central government debt, total (% of GDP)" 286 | }, 287 | "country": { 288 | "id": "CZ", 289 | "value": "Czech Republic" 290 | }, 291 | "value": null, 292 | "decimal": "1", 293 | "date": "1991" 294 | }, 295 | { 296 | "indicator": { 297 | "id": "GC.DOD.TOTL.GD.ZS", 298 | "value": "Central government debt, total (% of GDP)" 299 | }, 300 | "country": { 301 | "id": "CZ", 302 | "value": "Czech Republic" 303 | }, 304 | "value": null, 305 | "decimal": "1", 306 | "date": "1990" 307 | }, 308 | { 309 | "indicator": { 310 | "id": "GC.DOD.TOTL.GD.ZS", 311 | "value": "Central government debt, total (% of GDP)" 312 | }, 313 | "country": { 314 | "id": "CZ", 315 | "value": "Czech Republic" 316 | }, 317 | "value": null, 318 | "decimal": "1", 319 | "date": "1989" 320 | }, 321 | { 322 | "indicator": { 323 | "id": "GC.DOD.TOTL.GD.ZS", 324 | "value": "Central government debt, total (% of GDP)" 325 | }, 326 | "country": { 327 | "id": "CZ", 328 | "value": "Czech Republic" 329 | }, 330 | "value": null, 331 | "decimal": "1", 332 | "date": "1988" 333 | }, 334 | { 335 | "indicator": { 336 | "id": "GC.DOD.TOTL.GD.ZS", 337 | "value": "Central government debt, total (% of GDP)" 338 | }, 339 | "country": { 340 | "id": "CZ", 341 | "value": "Czech Republic" 342 | }, 343 | "value": null, 344 | "decimal": "1", 345 | "date": "1987" 346 | }, 347 | { 348 | "indicator": { 349 | "id": "GC.DOD.TOTL.GD.ZS", 350 | "value": "Central government debt, total (% of GDP)" 351 | }, 352 | "country": { 353 | "id": "CZ", 354 | "value": "Czech Republic" 355 | }, 356 | "value": null, 357 | "decimal": "1", 358 | "date": "1986" 359 | }, 360 | { 361 | "indicator": { 362 | "id": "GC.DOD.TOTL.GD.ZS", 363 | "value": "Central government debt, total (% of GDP)" 364 | }, 365 | "country": { 366 | "id": "CZ", 367 | "value": "Czech Republic" 368 | }, 369 | "value": null, 370 | "decimal": "1", 371 | "date": "1985" 372 | }, 373 | { 374 | "indicator": { 375 | "id": "GC.DOD.TOTL.GD.ZS", 376 | "value": "Central government debt, total (% of GDP)" 377 | }, 378 | "country": { 379 | "id": "CZ", 380 | "value": "Czech Republic" 381 | }, 382 | "value": null, 383 | "decimal": "1", 384 | "date": "1984" 385 | }, 386 | { 387 | "indicator": { 388 | "id": "GC.DOD.TOTL.GD.ZS", 389 | "value": "Central government debt, total (% of GDP)" 390 | }, 391 | "country": { 392 | "id": "CZ", 393 | "value": "Czech Republic" 394 | }, 395 | "value": null, 396 | "decimal": "1", 397 | "date": "1983" 398 | }, 399 | { 400 | "indicator": { 401 | "id": "GC.DOD.TOTL.GD.ZS", 402 | "value": "Central government debt, total (% of GDP)" 403 | }, 404 | "country": { 405 | "id": "CZ", 406 | "value": "Czech Republic" 407 | }, 408 | "value": null, 409 | "decimal": "1", 410 | "date": "1982" 411 | }, 412 | { 413 | "indicator": { 414 | "id": "GC.DOD.TOTL.GD.ZS", 415 | "value": "Central government debt, total (% of GDP)" 416 | }, 417 | "country": { 418 | "id": "CZ", 419 | "value": "Czech Republic" 420 | }, 421 | "value": null, 422 | "decimal": "1", 423 | "date": "1981" 424 | }, 425 | { 426 | "indicator": { 427 | "id": "GC.DOD.TOTL.GD.ZS", 428 | "value": "Central government debt, total (% of GDP)" 429 | }, 430 | "country": { 431 | "id": "CZ", 432 | "value": "Czech Republic" 433 | }, 434 | "value": null, 435 | "decimal": "1", 436 | "date": "1980" 437 | }, 438 | { 439 | "indicator": { 440 | "id": "GC.DOD.TOTL.GD.ZS", 441 | "value": "Central government debt, total (% of GDP)" 442 | }, 443 | "country": { 444 | "id": "CZ", 445 | "value": "Czech Republic" 446 | }, 447 | "value": null, 448 | "decimal": "1", 449 | "date": "1979" 450 | }, 451 | { 452 | "indicator": { 453 | "id": "GC.DOD.TOTL.GD.ZS", 454 | "value": "Central government debt, total (% of GDP)" 455 | }, 456 | "country": { 457 | "id": "CZ", 458 | "value": "Czech Republic" 459 | }, 460 | "value": null, 461 | "decimal": "1", 462 | "date": "1978" 463 | }, 464 | { 465 | "indicator": { 466 | "id": "GC.DOD.TOTL.GD.ZS", 467 | "value": "Central government debt, total (% of GDP)" 468 | }, 469 | "country": { 470 | "id": "CZ", 471 | "value": "Czech Republic" 472 | }, 473 | "value": null, 474 | "decimal": "1", 475 | "date": "1977" 476 | }, 477 | { 478 | "indicator": { 479 | "id": "GC.DOD.TOTL.GD.ZS", 480 | "value": "Central government debt, total (% of GDP)" 481 | }, 482 | "country": { 483 | "id": "CZ", 484 | "value": "Czech Republic" 485 | }, 486 | "value": null, 487 | "decimal": "1", 488 | "date": "1976" 489 | }, 490 | { 491 | "indicator": { 492 | "id": "GC.DOD.TOTL.GD.ZS", 493 | "value": "Central government debt, total (% of GDP)" 494 | }, 495 | "country": { 496 | "id": "CZ", 497 | "value": "Czech Republic" 498 | }, 499 | "value": null, 500 | "decimal": "1", 501 | "date": "1975" 502 | }, 503 | { 504 | "indicator": { 505 | "id": "GC.DOD.TOTL.GD.ZS", 506 | "value": "Central government debt, total (% of GDP)" 507 | }, 508 | "country": { 509 | "id": "CZ", 510 | "value": "Czech Republic" 511 | }, 512 | "value": null, 513 | "decimal": "1", 514 | "date": "1974" 515 | }, 516 | { 517 | "indicator": { 518 | "id": "GC.DOD.TOTL.GD.ZS", 519 | "value": "Central government debt, total (% of GDP)" 520 | }, 521 | "country": { 522 | "id": "CZ", 523 | "value": "Czech Republic" 524 | }, 525 | "value": null, 526 | "decimal": "1", 527 | "date": "1973" 528 | }, 529 | { 530 | "indicator": { 531 | "id": "GC.DOD.TOTL.GD.ZS", 532 | "value": "Central government debt, total (% of GDP)" 533 | }, 534 | "country": { 535 | "id": "CZ", 536 | "value": "Czech Republic" 537 | }, 538 | "value": null, 539 | "decimal": "1", 540 | "date": "1972" 541 | }, 542 | { 543 | "indicator": { 544 | "id": "GC.DOD.TOTL.GD.ZS", 545 | "value": "Central government debt, total (% of GDP)" 546 | }, 547 | "country": { 548 | "id": "CZ", 549 | "value": "Czech Republic" 550 | }, 551 | "value": null, 552 | "decimal": "1", 553 | "date": "1971" 554 | }, 555 | { 556 | "indicator": { 557 | "id": "GC.DOD.TOTL.GD.ZS", 558 | "value": "Central government debt, total (% of GDP)" 559 | }, 560 | "country": { 561 | "id": "CZ", 562 | "value": "Czech Republic" 563 | }, 564 | "value": null, 565 | "decimal": "1", 566 | "date": "1970" 567 | }, 568 | { 569 | "indicator": { 570 | "id": "GC.DOD.TOTL.GD.ZS", 571 | "value": "Central government debt, total (% of GDP)" 572 | }, 573 | "country": { 574 | "id": "CZ", 575 | "value": "Czech Republic" 576 | }, 577 | "value": null, 578 | "decimal": "1", 579 | "date": "1969" 580 | }, 581 | { 582 | "indicator": { 583 | "id": "GC.DOD.TOTL.GD.ZS", 584 | "value": "Central government debt, total (% of GDP)" 585 | }, 586 | "country": { 587 | "id": "CZ", 588 | "value": "Czech Republic" 589 | }, 590 | "value": null, 591 | "decimal": "1", 592 | "date": "1968" 593 | }, 594 | { 595 | "indicator": { 596 | "id": "GC.DOD.TOTL.GD.ZS", 597 | "value": "Central government debt, total (% of GDP)" 598 | }, 599 | "country": { 600 | "id": "CZ", 601 | "value": "Czech Republic" 602 | }, 603 | "value": null, 604 | "decimal": "1", 605 | "date": "1967" 606 | }, 607 | { 608 | "indicator": { 609 | "id": "GC.DOD.TOTL.GD.ZS", 610 | "value": "Central government debt, total (% of GDP)" 611 | }, 612 | "country": { 613 | "id": "CZ", 614 | "value": "Czech Republic" 615 | }, 616 | "value": null, 617 | "decimal": "1", 618 | "date": "1966" 619 | }, 620 | { 621 | "indicator": { 622 | "id": "GC.DOD.TOTL.GD.ZS", 623 | "value": "Central government debt, total (% of GDP)" 624 | }, 625 | "country": { 626 | "id": "CZ", 627 | "value": "Czech Republic" 628 | }, 629 | "value": null, 630 | "decimal": "1", 631 | "date": "1965" 632 | }, 633 | { 634 | "indicator": { 635 | "id": "GC.DOD.TOTL.GD.ZS", 636 | "value": "Central government debt, total (% of GDP)" 637 | }, 638 | "country": { 639 | "id": "CZ", 640 | "value": "Czech Republic" 641 | }, 642 | "value": null, 643 | "decimal": "1", 644 | "date": "1964" 645 | }, 646 | { 647 | "indicator": { 648 | "id": "GC.DOD.TOTL.GD.ZS", 649 | "value": "Central government debt, total (% of GDP)" 650 | }, 651 | "country": { 652 | "id": "CZ", 653 | "value": "Czech Republic" 654 | }, 655 | "value": null, 656 | "decimal": "1", 657 | "date": "1963" 658 | }, 659 | { 660 | "indicator": { 661 | "id": "GC.DOD.TOTL.GD.ZS", 662 | "value": "Central government debt, total (% of GDP)" 663 | }, 664 | "country": { 665 | "id": "CZ", 666 | "value": "Czech Republic" 667 | }, 668 | "value": null, 669 | "decimal": "1", 670 | "date": "1962" 671 | }, 672 | { 673 | "indicator": { 674 | "id": "GC.DOD.TOTL.GD.ZS", 675 | "value": "Central government debt, total (% of GDP)" 676 | }, 677 | "country": { 678 | "id": "CZ", 679 | "value": "Czech Republic" 680 | }, 681 | "value": null, 682 | "decimal": "1", 683 | "date": "1961" 684 | }, 685 | { 686 | "indicator": { 687 | "id": "GC.DOD.TOTL.GD.ZS", 688 | "value": "Central government debt, total (% of GDP)" 689 | }, 690 | "country": { 691 | "id": "CZ", 692 | "value": "Czech Republic" 693 | }, 694 | "value": null, 695 | "decimal": "1", 696 | "date": "1960" 697 | } 698 | ] 699 | ] -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/contacts.json: -------------------------------------------------------------------------------- 1 | { 2 | "ab": { 3 | "persons": [ 4 | { 5 | "contacts": [ 6 | { 7 | }, 8 | { 9 | "emailCapability": [ 10 | null, 11 | "0", 12 | null 13 | ], 14 | "emailIMEnabled": [ 15 | false, 16 | false, 17 | false 18 | ], 19 | "emails": [ 20 | null, 21 | "a@b.c", 22 | null 23 | ], 24 | "phones": [ 25 | null, 26 | null, 27 | null, 28 | null, 29 | null, 30 | null, 31 | null, 32 | null, 33 | null, 34 | null, 35 | null 36 | ] 37 | } 38 | ], 39 | "emails": [ 40 | null, 41 | null 42 | ], 43 | "phones": [ 44 | null, 45 | null, 46 | null, 47 | null, 48 | null, 49 | null, 50 | null, 51 | null, 52 | null, 53 | null 54 | ] 55 | } 56 | ] 57 | } 58 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/optionals.json: -------------------------------------------------------------------------------- 1 | { "recordProperty" : { "stringProperty": "foo", 2 | "intProperty": 2, 3 | "int64Property": 2222222222, 4 | "decimalProperty" : 2.5, 5 | "floatProperty": 222222222222222222222222222222222222222222, 6 | "boolProperty" : false, 7 | "dateProperty" : "2005/5/5", 8 | "guidProperty" : "{312B0D64-B27C-4ED2-B2A6-89EA6DD299F5}" }, 9 | "nullProperty" : null, 10 | "emptyStringProperty": "", 11 | "emptyArrayProperty" : [], 12 | "oneElementArrayProperty" : [2], 13 | "multipleElementsArrayProperty" : [2,4,5,6], 14 | "arrayOfObjects" : [ { "heterogeneousArrayProperty": [1,2,false,"foo"], "heterogeneousProperty": 2, "heterogeneousOptionalProperty": 2 }, 15 | { "heterogeneousArrayProperty": [1,2,false], "heterogeneousProperty": true, "heterogeneousOptionalProperty": false }, 16 | { "heterogeneousArrayProperty": [false], "heterogeneousProperty": "2005/5/5" } ], 17 | "optionalPrimitives" : [ { "optionalBecauseMissing" : 1, "optionalBecauseNull" : 2, "optionalBecauseEmptyString" : 3, "notOptional" : 4, "nullNotOptional" : null }, 18 | { "optionalBecauseNull" : null, "optionalBecauseEmptyString" : "", "notOptional" : "5" } ], 19 | "optionalRecords" : [ { "optionalBecauseMissing" : {"a":1}, "optionalBecauseNull" : {"a":1}, "optionalBecauseEmptyString" : {"a":2}, "notOptional" : {"a":3} }, 20 | { "optionalBecauseNull" : null, "optionalBecauseEmptyString" : "", "notOptional" : {"a":4} } ], 21 | "heterogeneousArray" : [ { "a" : 1 }, 2, false, [5, 6, 7], [8, false, { "a": 3 }] ], 22 | "heterogeneousRecords" : [ { "b" : { "a" : 1 }}, { "b" : 2}, { "b" : false}, { "b" : [5, 6, 7]}, { "b" : [8, false, { "a": 3 }] } ] 23 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/projects.json: -------------------------------------------------------------------------------- 1 | { 2 | "ordercontainer": { 3 | "backgrounds": { 4 | "background": { "title": { "text": "purple stars" } } 5 | }, 6 | "project": { "title": { "text": "Avery" } } 7 | } 8 | } -------------------------------------------------------------------------------- /src/MiniJson.Tests/TestCases/reddit.json: -------------------------------------------------------------------------------- 1 | {"kind": "Listing", "data": {"modhash": "", "children": [{"kind": "t1", "data": {"subreddit_id": "t5_2u479", "link_title": "I'm fundraising for another billboard, this one outside the FCC offices, to help save net neutrality.", "banned_by": null, "subreddit": "Stand", "link_author": "kn0thing", "likes": null, "replies": null, "saved": false, "id": "ch2xdud", "gilded": 0, "author": "kn0thing", "parent_id": "t1_ch2weca", "approved_by": null, "body": "I love what YOU'RE DOING. Thanks a ton.", "edited": false, "author_flair_css_class": null, "downs": 0, "body_html": "<div class=\"md\"><p>I love what YOU&#39;RE DOING. Thanks a ton.</p>\n</div>", "link_id": "t3_2424px", "score_hidden": false, "name": "t1_ch2xdud", "created": 1398586311.0, "author_flair_text": null, "link_url": "https://www.crowdtilt.com/campaigns/save-net-neutrality-billboard-in-fccs-backyard", "created_utc": 1398557511.0, "ups": 3, "num_reports": null, "distinguished": null}}], "after": "t1_ch2xdud", "before": null}} -------------------------------------------------------------------------------- /src/MiniJson.Tests/coverage.cmd: -------------------------------------------------------------------------------- 1 | ..\packages\OpenCover.4.6.166\tools\OpenCover.Console.exe -target:"bin\Debug\MiniJson.Tests.exe" -filter:"+[MiniJson]*" 2 | ..\packages\ReportGenerator.2.1.8.0\tools\ReportGenerator.exe -reports:results.xml -targetdir:bin\Debug\coverage 3 | .\bin\Debug\coverage\index.htm 4 | -------------------------------------------------------------------------------- /src/MiniJson.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/MiniJson.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniJson", "MiniJson\MiniJson.fsproj", "{68C7233E-B616-4233-A3B8-6881F11DFB2F}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniJson.Reference", "MiniJson.Reference\MiniJson.Reference.fsproj", "{821C50D0-C33E-4CEB-B1C5-767CA682083A}" 9 | EndProject 10 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniJson.Tests", "MiniJson.Tests\MiniJson.Tests.fsproj", "{387B94CC-4E4D-4F3C-BF80-5916C895E404}" 11 | EndProject 12 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniJson.Paket", "MiniJson.Paket\MiniJson.Paket.fsproj", "{C3711D8D-21A1-4E9A-B130-F4B4193C24E6}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F5AB18E5-05DD-47B6-A65D-8CC66EF5E7DF}" 15 | ProjectSection(SolutionItems) = preProject 16 | ..\.gitattributes = ..\.gitattributes 17 | ..\.gitignore = ..\.gitignore 18 | ..\LICENSE = ..\LICENSE 19 | MiniJson.NuGetPackage\M3.MiniJson.nuspec = MiniJson.NuGetPackage\M3.MiniJson.nuspec 20 | ..\README.md = ..\README.md 21 | EndProjectSection 22 | EndProject 23 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniJson.NuGet", "MiniJson.NuGet\MiniJson.NuGet.fsproj", "{80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniJson.CSharp", "MiniJson.CSharp\MiniJson.CSharp.csproj", "{ADF5BA44-E250-4C08-865E-793C009C9A5B}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Debug|x64 = Debug|x64 31 | Debug|x86 = Debug|x86 32 | Release|Any CPU = Release|Any CPU 33 | Release|x64 = Release|x64 34 | Release|x86 = Release|x86 35 | EndGlobalSection 36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 37 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|x64.ActiveCfg = Debug|x64 40 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|x64.Build.0 = Debug|x64 41 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|x86.ActiveCfg = Debug|x86 42 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Debug|x86.Build.0 = Debug|x86 43 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|x64.ActiveCfg = Release|x64 46 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|x64.Build.0 = Release|x64 47 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|x86.ActiveCfg = Release|x86 48 | {68C7233E-B616-4233-A3B8-6881F11DFB2F}.Release|x86.Build.0 = Release|x86 49 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|x64.ActiveCfg = Debug|x64 52 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|x64.Build.0 = Debug|x64 53 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|x86.ActiveCfg = Debug|x86 54 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Debug|x86.Build.0 = Debug|x86 55 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|x64.ActiveCfg = Release|x64 58 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|x64.Build.0 = Release|x64 59 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|x86.ActiveCfg = Release|x86 60 | {821C50D0-C33E-4CEB-B1C5-767CA682083A}.Release|x86.Build.0 = Release|x86 61 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|x64.ActiveCfg = Debug|x64 64 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|x64.Build.0 = Debug|x64 65 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|x86.ActiveCfg = Debug|x86 66 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Debug|x86.Build.0 = Debug|x86 67 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|Any CPU.Build.0 = Release|Any CPU 69 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|x64.ActiveCfg = Release|x64 70 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|x64.Build.0 = Release|x64 71 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|x86.ActiveCfg = Release|x86 72 | {387B94CC-4E4D-4F3C-BF80-5916C895E404}.Release|x86.Build.0 = Release|x86 73 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 74 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 75 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|x64.ActiveCfg = Debug|x64 76 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|x64.Build.0 = Debug|x64 77 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|x86.ActiveCfg = Debug|x86 78 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Debug|x86.Build.0 = Debug|x86 79 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 80 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|Any CPU.Build.0 = Release|Any CPU 81 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|x64.ActiveCfg = Release|x64 82 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|x64.Build.0 = Release|x64 83 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|x86.ActiveCfg = Release|x86 84 | {C3711D8D-21A1-4E9A-B130-F4B4193C24E6}.Release|x86.Build.0 = Release|x86 85 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 86 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|Any CPU.Build.0 = Debug|Any CPU 87 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|x64.ActiveCfg = Debug|x64 88 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|x64.Build.0 = Debug|x64 89 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|x86.ActiveCfg = Debug|x86 90 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Debug|x86.Build.0 = Debug|x86 91 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|Any CPU.ActiveCfg = Release|Any CPU 92 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|Any CPU.Build.0 = Release|Any CPU 93 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|x64.ActiveCfg = Release|x64 94 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|x64.Build.0 = Release|x64 95 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|x86.ActiveCfg = Release|x86 96 | {80BDD5EA-BD91-479B-8D6F-1DCD1A18F49A}.Release|x86.Build.0 = Release|x86 97 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|x64.ActiveCfg = Debug|Any CPU 100 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|x64.Build.0 = Debug|Any CPU 101 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|x86.ActiveCfg = Debug|Any CPU 102 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Debug|x86.Build.0 = Debug|Any CPU 103 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU 104 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|Any CPU.Build.0 = Release|Any CPU 105 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|x64.ActiveCfg = Release|Any CPU 106 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|x64.Build.0 = Release|Any CPU 107 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|x86.ActiveCfg = Release|Any CPU 108 | {ADF5BA44-E250-4C08-865E-793C009C9A5B}.Release|x86.Build.0 = Release|Any CPU 109 | EndGlobalSection 110 | GlobalSection(SolutionProperties) = preSolution 111 | HideSolutionNode = FALSE 112 | EndGlobalSection 113 | EndGlobal 114 | -------------------------------------------------------------------------------- /src/MiniJson/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/MiniJson/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | namespace MiniJson.AssemblyInfo 17 | 18 | open System.Reflection 19 | open System.Runtime.InteropServices 20 | 21 | [] 22 | [] 23 | do 24 | () 25 | -------------------------------------------------------------------------------- /src/MiniJson/MiniJson.Adaptor.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | 17 | /// MiniJson aims to be a minimal yet conforming JSON parser with reasonable performance and decent error reporting 18 | /// JSON Specification: http://json.org/ 19 | /// JSON Lint : http://jsonlint.com/ 20 | /// 21 | /// MiniJson.Adaptor provides adaptor functions to simplify MiniJson usage for non-F# libraries 22 | /// 23 | namespace MiniJson.Adaptor 24 | 25 | open System 26 | open System.Collections.Generic 27 | open System.Diagnostics 28 | open System.Dynamic 29 | open System.Linq 30 | open System.Runtime.Serialization 31 | open System.Security.Permissions 32 | 33 | open MiniJson.JsonModule 34 | open MiniJson.DynamicJsonModule 35 | 36 | /// JsonParseException (msg, pos) is raised during parser errors 37 | exception JsonParseException of string*int 38 | with 39 | /// Gets the error message, the message describes any potential parse failures 40 | member x.ErrorMessage : string = x.Data0 41 | 42 | /// Gets the position where parsing stopped. Note the position might point beyond end of string 43 | member x.ErrorPosition : int = x.Data1 44 | 45 | override x.ToString () : string = x.Data0 46 | 47 | /// JsonDynamicObject implements DynamicObject which allows C# (and VB) to 48 | /// explore JSON values using dynamic lookup 49 | [] 50 | [] 51 | type JsonDynamicObject(jsonPath : JsonPath) = 52 | inherit DynamicObject() 53 | 54 | override x.Equals (o : obj) : bool = 55 | match o with 56 | | :? JsonDynamicObject as ox -> jsonPath.Equals (ox.GetJsonPath ()) 57 | | _ -> false 58 | 59 | override x.GetHashCode () : int = jsonPath.GetHashCode () 60 | 61 | override x.ToString () : string = jsonPath.AsString 62 | 63 | override x.GetDynamicMemberNames () : seq = upcast jsonPath.Names 64 | 65 | override x.TryGetIndex (binder : GetIndexBinder, indexes : obj [], result : obj byref) : bool = 66 | let index = 67 | if indexes.Length = 1 then 68 | match indexes.[0] with 69 | | :? int as i -> i 70 | | _ -> Int32.MinValue 71 | else 72 | Int32.MinValue 73 | if index > Int32.MinValue then 74 | let next = jsonPath.[index] 75 | result <- JsonDynamicObject next 76 | true 77 | else 78 | base.TryGetIndex (binder, indexes, &result) 79 | 80 | override x.TryGetMember (binder : GetMemberBinder, result : obj byref) : bool = 81 | let next = jsonPath.Get binder.Name 82 | result <- JsonDynamicObject next 83 | true 84 | 85 | override x.TryConvert (binder : ConvertBinder, result : obj byref) : bool = 86 | let rt = binder.ReturnType 87 | if rt = typeof then 88 | result <- jsonPath.AsString 89 | true 90 | elif rt = typeof then 91 | result <- jsonPath.AsFloat 92 | true 93 | elif rt = typeof then 94 | result <- jsonPath.AsBool 95 | true 96 | else 97 | base.TryConvert (binder, &result) 98 | 99 | // These are methods not properties in order to not collide with the dynamic lookup of JSON properties 100 | 101 | /// Gets the JSON Path object 102 | member x.GetJsonPath () : JsonPath = jsonPath 103 | 104 | /// Gets the JSON Path object as string 105 | member x.GetJsonPathString () : string = jsonPath.ToString () 106 | 107 | /// Gets the referenced JSON object 108 | member x.GetJson () : Json = 109 | match jsonPath with 110 | | PathOk (json, _) -> json 111 | | PathError _ -> JsonNull 112 | 113 | /// Gets the referenced JSON object as string 114 | member x.GetJsonString indent : string = toString indent (x.GetJson ()) 115 | 116 | /// Gets the referenced JSON object as string 117 | member x.GetJsonString () : string = x.GetJsonString false 118 | 119 | /// Gets the number of child items the referenced JSON object has 120 | member x.GetLength () : int = jsonPath.Length 121 | 122 | /// Returns true if the referenced JSON object exists 123 | member x.HasValue () : bool = jsonPath.HasValue 124 | 125 | /// Converts the referenced JSON object to float (double), 126 | /// if conversion fails returns 'f' 127 | member x.ConvertToFloat f : float = jsonPath.ConvertToFloat f 128 | 129 | /// Returns children of referenced JSON object 130 | member x.GetChildren () : JsonDynamicObject [] = 131 | jsonPath.Children |> Array.map JsonDynamicObject 132 | 133 | 134 | /// JsonParser parses a JSON string 135 | [] 136 | [] 137 | type JsonParser(input : string, extendedErrorInfo : bool) = 138 | let result = parse extendedErrorInfo input 139 | 140 | member internal x.InternalResult : ParseResult = result 141 | 142 | override x.Equals (o : obj) : bool = 143 | match o with 144 | | :? JsonParser as ox -> result.Equals (ox.InternalResult) 145 | | _ -> false 146 | 147 | override x.GetHashCode () : int = result.GetHashCode () 148 | 149 | override x.ToString () : string = sprintf "%A" result 150 | 151 | /// Returns true when parse succeeded 152 | member x.Success : bool = match result with Success _ -> true | _ -> false 153 | 154 | /// Returns true when parse failed 155 | member x.Failure : bool = not x.Success 156 | 157 | /// Gets the parsed JSON object, throws JsonParseException on parse failure 158 | member x.Result : Json = 159 | match result with 160 | | Success json -> json 161 | | Failure (msg, pos) -> raise (JsonParseException (msg, pos)) 162 | 163 | /// Gets the parse message, the message describes any potential parse failures 164 | member x.ParseMessage : string = 165 | match result with 166 | | Success _ -> "No errors detected during parse" 167 | | Failure (msg, _) -> msg 168 | 169 | /// Gets the position where parsing stopped. Note the position might point beyond end of string 170 | member x.ParsePosition : int = 171 | match result with 172 | | Success _ -> input.Length 173 | | Failure (_, pos) -> pos 174 | 175 | /// Gets the parsed JSON object as DynamicObject, throws JsonParseException on parse failure 176 | member x.DynamicResult : JsonDynamicObject = 177 | let json = x.Result 178 | let jsonPath = makePath json 179 | JsonDynamicObject jsonPath 180 | -------------------------------------------------------------------------------- /src/MiniJson/MiniJson.Dynamic.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | 17 | /// MiniJson aims to be a minimal yet conforming JSON parser with reasonable performance and decent error reporting 18 | /// JSON Specification: http://json.org/ 19 | /// JSON Lint : http://jsonlint.com/ 20 | /// 21 | /// MiniJson.DynamicJsonModule contains functionality query a JSON document using 22 | /// op_Dynamic ( ? ) in F# 23 | /// 24 | /// 25 | /// Example: 26 | /// -------- 27 | /// let root = json.Query 28 | /// 29 | /// for i = 0 to root.Length - 1 do 30 | /// let v = root.[i] 31 | /// let id = v?id.AsString 32 | /// let name = v?name.AsString 33 | /// let age = v?age.AsFloat 34 | /// printfn "Record - %d: id:%s, name:%s, age:%f" i id name age 35 | #if PUBLIC_MINIJSON 36 | module MiniJson.DynamicJsonModule 37 | #else 38 | // Due to what seems to be an issue with the F# compiler preventing 39 | // access to internal operator ? from within the same assembly 40 | // define INTERNAL_MINIJSON_WORKAROUND to suppress internalizing of 41 | // MiniJson. 42 | #if INTERNAL_MINIJSON_WORKAROUND 43 | module Internal.MiniJson.DynamicJsonModule 44 | #else 45 | module internal Internal.MiniJson.DynamicJsonModule 46 | #endif 47 | #endif 48 | open System 49 | open System.Globalization 50 | open System.Text 51 | 52 | open JsonModule 53 | 54 | /// Represents a valid JSON element query 55 | type JsonQuery = 56 | /// (name) - Represents a valid JSON element object property query 57 | | QueryProperty of string 58 | /// (index) - Represents a valid JSON element array indexing query 59 | | QueryIndexOf of int 60 | 61 | /// Represents an invalid JSON element query 62 | type JsonQueryError = 63 | /// (name) - Represents a invalid JSON element object property query as the referenced element wasn't an object 64 | | ErrorNotObject of string 65 | /// (name) - Represents a invalid JSON element array indexing query as the referenced element wasn't an array 66 | | ErrorNotIndexable of int 67 | /// (name) - Represents a invalid JSON element object property query as the property didn't exist 68 | | ErrorUnknownProperty of string 69 | /// (name) - Represents a invalid JSON element array indexing query as the index was out of bounds 70 | | ErrorIndexOutBounds of int 71 | 72 | /// (json, parents) - Represents a valid Path to a JSON element 73 | type Path = Json*(JsonQuery*Json) list 74 | 75 | /// (errors, json, parents) - Represents an invalid Path to a JSON element 76 | type InvalidPath = JsonQueryError list*Json*(JsonQuery*Json) list 77 | 78 | module internal Details = 79 | let inline ch (sb : StringBuilder) (c : char) : unit = ignore <| sb.Append c 80 | let inline str (sb : StringBuilder) (s : string) : unit = ignore <| sb.Append s 81 | let inline ii (sb : StringBuilder) (i : int) : unit = ignore <| sb.Append i 82 | 83 | let rec appendParents (sb : StringBuilder) = function 84 | | [] -> () 85 | | p::ps -> 86 | // Tail-recursiveness not so important here as we expect only a few parents 87 | appendParents sb ps 88 | match p with 89 | | (QueryProperty name, _) -> ch sb '.'; str sb name 90 | | (QueryIndexOf i, _) -> str sb ".["; ii sb i; ch sb ']' 91 | 92 | let rec appendErrors (sb : StringBuilder) = function 93 | | [] -> () 94 | | e::es -> 95 | // Tail-recursiveness not so important here as we expect only a few errors 96 | appendErrors sb es 97 | match e with 98 | | ErrorNotObject name 99 | | ErrorUnknownProperty name -> ch sb '!'; str sb name 100 | | ErrorNotIndexable i 101 | | ErrorIndexOutBounds i -> str sb "!["; ii sb i; ch sb ']' 102 | 103 | open Details 104 | 105 | [] 106 | [] 107 | /// Represents a JSON scalar (null, bool, number, string or error) 108 | type JsonScalar = 109 | /// (json, parents) - Represents a null scalar 110 | | ScalarNull of Path 111 | /// (json, parents) - Represents a bool scalar 112 | | ScalarBoolean of Path*bool 113 | /// (json, parents) - Represents a number (float) scalar 114 | | ScalarNumber of Path*float 115 | /// (json, parents) - Represents a string scalar 116 | | ScalarString of Path*string 117 | /// (json, parents) - An errors representing that the referenced element is not a scalar 118 | | ScalarNotScalar of Path 119 | /// (json, parents) - An errors representing that the referenced element doesn't exist 120 | | ScalarInvalidPath of InvalidPath 121 | 122 | /// Returns true if it's an invalid scalar element 123 | member x.IsError : bool = 124 | match x with 125 | | ScalarNull _ 126 | | ScalarBoolean _ 127 | | ScalarNumber _ 128 | | ScalarString _ -> false 129 | | ScalarNotScalar _ 130 | | ScalarInvalidPath _ -> true 131 | 132 | /// Returns true if it's a valid scalar element, 133 | /// if the scalar element couldn't be converted successfully returns false. 134 | member x.HasValue : bool = 135 | match x with 136 | | ScalarNull _ -> false 137 | | ScalarBoolean _ 138 | | ScalarNumber _ 139 | | ScalarString _ -> true 140 | | ScalarNotScalar _ 141 | | ScalarInvalidPath _ -> false 142 | 143 | /// Returns a boolean representation of the scalar element, 144 | /// if the scalar element couldn't be converted successfully returns false. 145 | member x.AsBool : bool = 146 | match x with 147 | | ScalarNull _ -> false 148 | | ScalarBoolean (_,b) -> b 149 | | ScalarNumber (_,n) -> n <> 0.0 150 | | ScalarString (_,s) -> s.Length > 0 151 | | ScalarNotScalar _ 152 | | ScalarInvalidPath _ -> false 153 | 154 | /// Returns a float representation of the scalar element, 155 | /// if the scalar element couldn't be converted successfully returns 0.0. 156 | member x.AsFloat : float = 157 | x.ConvertToFloat 0. 158 | 159 | /// Returns a string representation of the scalar element. 160 | /// Null values and errors are represented as "" 161 | member x.AsString : string = 162 | x.AsStringImpl false 163 | 164 | /// Returns the expanded string representation of the scalar element. 165 | /// Expanded means null values are represented as 'null' and full error error infos are generated for errors 166 | member x.AsExpandedString : string = 167 | x.AsStringImpl true 168 | 169 | /// Returns a float representation of the scalar element. 170 | /// This allows the user to specify the value to return if the scalar element couldn't be converted successfully 171 | member x.ConvertToFloat (defaultTo : float) : float = 172 | match x with 173 | | ScalarNull _ -> 0. 174 | | ScalarBoolean (_,b) -> if b then 1. else 0. 175 | | ScalarNumber (_,n) -> n 176 | | ScalarString (_,s) -> 177 | let b,f = Double.TryParse (s, NumberStyles.Float, CultureInfo.InvariantCulture) 178 | if b then f else defaultTo 179 | | ScalarNotScalar _ 180 | | ScalarInvalidPath _ -> defaultTo 181 | 182 | member internal x.AsStringImpl (expand : bool) : string = 183 | match x with 184 | | ScalarNull _ -> if expand then "null" else "" 185 | | ScalarBoolean (_,b) -> if b then "true" else "false" 186 | | ScalarNumber (_,n) -> n.ToString CultureInfo.InvariantCulture 187 | | ScalarString (_,s) -> s 188 | | ScalarNotScalar path -> 189 | if expand then 190 | let json, parents = path 191 | let sb = StringBuilder ("NotScalar: root") 192 | appendParents sb parents 193 | sb.ToString () 194 | else 195 | "" 196 | | ScalarInvalidPath invalidPath -> 197 | if expand then 198 | let errors, json, parents = invalidPath 199 | let sb = StringBuilder ("InvalidPath: root") 200 | appendParents sb parents 201 | appendErrors sb errors 202 | sb.ToString () 203 | else 204 | "" 205 | 206 | override x.ToString () : string = 207 | x.AsStringImpl true 208 | 209 | /// Represents a path to a JSON element 210 | type JsonPath = 211 | /// (json, parents) - Holds the current json element and its parents 212 | | PathOk of Path 213 | /// (errors, json, parents) - Holds the invalid path elements, the last valid json element and its parents 214 | | PathError of InvalidPath 215 | 216 | /// Evaluates the path producing the referenced scalar element (scalar meaning null, bool, number, string or error) 217 | member x.Eval : JsonScalar = 218 | match x with 219 | | PathOk path -> 220 | let json, _ = path 221 | match json with 222 | | JsonNull -> ScalarNull path 223 | | JsonBoolean b -> ScalarBoolean (path, b) 224 | | JsonNumber n -> ScalarNumber (path, n) 225 | | JsonString s -> ScalarString (path, s) 226 | | _ -> ScalarNotScalar path 227 | | PathError invalidPath -> ScalarInvalidPath invalidPath 228 | 229 | /// Returns the Length of the referenced array element, 230 | /// if it's not an array returns 0 231 | member x.Length : int = 232 | match x with 233 | | PathOk (json, _) -> 234 | match json with 235 | | JsonNull 236 | | JsonBoolean _ 237 | | JsonNumber _ 238 | | JsonString _ -> 0 239 | | JsonObject ms -> ms.Length 240 | | JsonArray vs -> vs.Length 241 | | PathError _ -> 0 242 | 243 | /// Returns a path to the element at the index of the referenced array element, 244 | /// if it's not an array or out of bounds returns a PathError 245 | member x.Item (i : int) : JsonPath = 246 | match x with 247 | | PathOk (json, parents) -> 248 | match json with 249 | | JsonNull 250 | | JsonBoolean _ 251 | | JsonNumber _ 252 | | JsonString _ -> 253 | PathError ([ErrorNotIndexable i], json, parents) 254 | | JsonObject ms -> 255 | if i >= 0 && i < ms.Length then 256 | let _, v = ms.[i] 257 | PathOk (v, (QueryIndexOf i, json)::parents) 258 | else 259 | PathError ([ErrorIndexOutBounds i], json, parents) 260 | | JsonArray vs -> 261 | if i >= 0 && i < vs.Length then 262 | let v = vs.[i] 263 | PathOk (v, (QueryIndexOf i, json)::parents) 264 | else 265 | PathError ([ErrorIndexOutBounds i], json, parents) 266 | | PathError (errors, json, parents) -> 267 | PathError ((ErrorNotIndexable i)::errors, json, parents) 268 | 269 | 270 | /// Returns all property names in order (and with potential duplicates) 271 | /// if it's not an object or named element doesn't exists returns an empty array 272 | member x.Names : string [] = 273 | match x with 274 | | PathOk (json, parents) -> 275 | match json with 276 | | JsonNull 277 | | JsonBoolean _ 278 | | JsonNumber _ 279 | | JsonString _ 280 | | JsonArray _ -> 281 | [||] 282 | | JsonObject ms -> 283 | ms |> Array.map (fun (k,_) -> k) 284 | | PathError (errors, json, parents) -> 285 | [||] 286 | 287 | /// Returns all children as an array of values in order (and with potential duplicates) 288 | /// if it's not an object, array or named element doesn't exists returns an empty array 289 | member x.Children : JsonPath [] = 290 | match x with 291 | | PathOk (json, parents) -> 292 | match json with 293 | | JsonNull 294 | | JsonBoolean _ 295 | | JsonNumber _ 296 | | JsonString _ -> 297 | [||] 298 | | JsonArray vs -> 299 | vs |> Array.mapi (fun i v -> PathOk (v, (QueryIndexOf i, json)::parents)) 300 | | JsonObject ms -> 301 | ms |> Array.mapi (fun i (_,v) -> PathOk (v, (QueryIndexOf i, json)::parents)) 302 | | PathError (errors, json, parents) -> 303 | [||] 304 | 305 | 306 | /// Returns a path to the named element of the referenced object element, 307 | /// if it's not an object or named element doesn't exists returns a PathError 308 | member x.Get (name : string) : JsonPath = 309 | match x with 310 | | PathOk (json, parents) -> 311 | match json with 312 | | JsonNull 313 | | JsonBoolean _ 314 | | JsonNumber _ 315 | | JsonString _ 316 | | JsonArray _ -> 317 | PathError ([ErrorNotObject name], json, parents) 318 | | JsonObject ms -> 319 | let rec find i = 320 | if i < ms.Length then 321 | let k,v = ms.[i] 322 | if k = name then 323 | PathOk (v, (QueryProperty name ,json)::parents) 324 | else 325 | find (i + 1) 326 | else 327 | PathError ([ErrorUnknownProperty name], json, parents) 328 | find 0 329 | | PathError (errors, json, parents) -> 330 | PathError ((ErrorNotObject name)::errors, json, parents) 331 | 332 | /// Returns true if the path elements references a valid scalar element, 333 | /// if the referenced scalar element couldn't be converted successfully returns false. 334 | member x.HasValue : bool = 335 | x.Eval.HasValue 336 | 337 | /// Returns a boolean representation of the referenced scalar element, 338 | /// if the referenced scalar element couldn't be converted successfully returns false. 339 | member x.AsBool : bool = 340 | x.Eval.AsBool 341 | 342 | /// Returns a float representation of the referenced scalar element, 343 | /// if the referenced scalar element couldn't be converted successfully returns 0.0. 344 | member x.AsFloat : float = 345 | x.Eval.AsFloat 346 | 347 | /// Returns a string representation of the referenced scalar element, 348 | /// null values and errors are represented as "". 349 | member x.AsString : string = 350 | x.Eval.AsString 351 | 352 | /// Returns the expanded string representation of the referenced scalar element. 353 | /// Expanded means null values are represented as 'null' and full error error infos are generated for errors. 354 | member x.AsExpandedString : string = 355 | x.Eval.AsExpandedString 356 | 357 | /// Returns a float representation of the referenced scalar element. 358 | /// This allows the user to specify the value to return if the referenced scalar element couldn't be converted successfully. 359 | /// @defaultTo - The float to default to if the referenced scalar element couldn't be converted successfully. 360 | member x.ConvertToFloat (defaultTo : float) : float = 361 | x.Eval.ConvertToFloat defaultTo 362 | 363 | override x.ToString () : string = 364 | match x with 365 | | PathOk (_, parents) -> 366 | let sb = StringBuilder ("PathOk: root") 367 | appendParents sb parents 368 | sb.ToString () 369 | | PathError (errors, _, parents) -> 370 | let sb = StringBuilder ("PathError: root") 371 | appendParents sb parents 372 | appendErrors sb errors 373 | sb.ToString () 374 | 375 | /// Returns a path to the named element of the referenced object element, 376 | /// if it's not an object or named element doesn't exists returns a PathError 377 | static member inline ( ? ) (path : JsonPath, name : string) : JsonPath = 378 | path.Get name 379 | 380 | /// Evaluates the path producing the referenced scalar element (scalar meaning null, bool, number, string or error) 381 | static member inline ( !! ) (path : JsonPath) : JsonScalar = 382 | path.Eval 383 | 384 | /// Creates a JsonPath object from a JSON document 385 | /// @json - A JSON document 386 | let inline makePath (json : Json) = PathOk (json, []) 387 | 388 | type Json with 389 | 390 | /// Creates a JsonPath object from a JSON document 391 | member x.Query : JsonPath = makePath x 392 | 393 | module Infixes = 394 | 395 | // These operators are provided as a workaround for regression in F#4.1 396 | // https://github.com/Microsoft/visualfsharp/issues/2416 397 | // (Fixed as 2017-04-27) 398 | 399 | /// Returns a path to the named element of the referenced object element, 400 | /// if it's not an object or named element doesn't exists returns a PathError 401 | let inline ( ? ) (path : JsonPath) (name : string) : JsonPath = 402 | path.Get name 403 | 404 | /// Evaluates the path producing the referenced scalar element (scalar meaning null, bool, number, string or error) 405 | let inline ( !! ) (path : JsonPath) : JsonScalar = 406 | path.Eval 407 | -------------------------------------------------------------------------------- /src/MiniJson/MiniJson.fs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------------------------- 2 | // Copyright 2015 Mårten Rånge 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ---------------------------------------------------------------------------------------------- 16 | 17 | /// MiniJson aims to be a minimal yet conforming JSON parser with reasonable performance and decent error reporting 18 | /// JSON Specification: http://json.org/ 19 | /// JSON Lint : http://jsonlint.com/ 20 | /// 21 | /// MiniJson.JsonModule contains functionality to parse and serialize a JSON document 22 | /// 23 | /// 24 | /// Example: 25 | /// -------- 26 | /// let jsonText = """[{"id":"123", "name":"Mr. Big", "age":30}, {"id":"123", "name":"Mr. X"}]""" 27 | /// 28 | /// match parse true jsonText with // true for full error-info 29 | /// | Failure (msg, pos) -> printfn "Failure@%d\n%s" pos msg 30 | /// | Success json -> 31 | /// printfn "Success\n%s" <| toString true json // true to indent JSON 32 | #if PUBLIC_MINIJSON 33 | module MiniJson.JsonModule 34 | #else 35 | // Due to what seems to be an issue with the F# compiler preventing 36 | // access to internal operator ? from within the same assembly 37 | // define INTERNAL_MINIJSON_WORKAROUND to suppress internalizing of 38 | // MiniJson. 39 | #if INTERNAL_MINIJSON_WORKAROUND 40 | module Internal.MiniJson.JsonModule 41 | #else 42 | module internal Internal.MiniJson.JsonModule 43 | #endif 44 | #endif 45 | open System 46 | open System.Collections.Generic 47 | open System.Diagnostics 48 | open System.Globalization 49 | open System.Text 50 | 51 | module internal Tokens = 52 | [] 53 | let Null = "null" 54 | 55 | [] 56 | let True = "true" 57 | 58 | [] 59 | let False = "false" 60 | 61 | [] 62 | let Char = "char" 63 | 64 | [] 65 | let Digit = "digit" 66 | 67 | [] 68 | let HexDigit = "hexdigit" 69 | 70 | [] 71 | let EOS = "EOS" 72 | 73 | [] 74 | let NewLine = "NEWLINE" 75 | 76 | [] 77 | let Escapes = "\"\\/bfnrtu" 78 | 79 | [] 80 | let ValuePreludes = "\"{[-" 81 | 82 | [] 83 | let RootValuePreludes = "{[" 84 | 85 | module internal ToStringDetails = 86 | let nonPrintableChars = 87 | [| 88 | for i in 0..31 -> 89 | match char i with 90 | | '\b' -> @"\b" 91 | | '\f' -> @"\f" 92 | | '\n' -> @"\n" 93 | | '\r' -> @"\r" 94 | | '\t' -> @"\t" 95 | | ch -> sprintf "\u%04X" i 96 | |] 97 | 98 | open ToStringDetails 99 | /// Represents a JSON document 100 | type Json = 101 | /// () - Represents a JSON null value 102 | | JsonNull 103 | /// (value) - Represents a JSON boolean value 104 | | JsonBoolean of bool 105 | /// (value) - Represents a JSON number value 106 | | JsonNumber of float 107 | /// (value) - Represents a JSON string value 108 | | JsonString of string 109 | /// (values) - Represents a JSON array value 110 | | JsonArray of Json [] 111 | /// (members) - Represents a JSON object value 112 | | JsonObject of (string*Json) [] 113 | 114 | /// Converts a JSON document into a string 115 | /// @doIndent : True to indent 116 | member x.ToString (doIndent : bool) : string = 117 | let sb = StringBuilder () 118 | 119 | let newline, indent, inc, dec = 120 | let doNothing () = () 121 | if doIndent then 122 | let current = ref 0 123 | 124 | let newline () = ignore <| sb.AppendLine () 125 | let indent () = ignore <| sb.Append (' ', !current) 126 | let inc () = current := !current + 2 127 | let dec () = current := !current - 2 128 | 129 | newline, indent, inc, dec 130 | else 131 | doNothing, doNothing, doNothing, doNothing 132 | 133 | let inline str (s : string) = ignore <| sb.Append s 134 | let inline ch (c : char) = ignore <| sb.Append c 135 | let inline num (f : float) = 136 | if Double.IsNaN f then 137 | ignore <| sb.AppendFormat "\"NaN\"" // JSON numbers doesn't support NaN 138 | elif Double.IsPositiveInfinity f then 139 | ignore <| sb.AppendFormat "\"+Inf\"" // JSON numbers doesn't support +Inf 140 | elif Double.IsNegativeInfinity f then 141 | ignore <| sb.AppendFormat "\"-Inf\"" // JSON numbers doesn't support -Inf 142 | else 143 | ignore <| sb.AppendFormat (CultureInfo.InvariantCulture, "{0:G}", f) 144 | 145 | let estr (s : string) = 146 | ch '"' 147 | let e = s.Length - 1 148 | for i = 0 to e do 149 | match s.[i] with 150 | | '\"' -> str @"\""" 151 | | '\\' -> str @"\\" 152 | | '/' -> str @"\/" 153 | | c when c < ' ' -> str ToStringDetails.nonPrintableChars.[int c] 154 | | c -> ch c 155 | ch '"' 156 | 157 | let values b e (vs : 'T array) (a : 'T -> unit) = 158 | ch b 159 | newline () 160 | inc () 161 | let ee = vs.Length - 1 162 | for i = 0 to ee do 163 | let v = vs.[i] 164 | indent () 165 | a v 166 | if i < ee then 167 | ch ',' 168 | newline () 169 | dec () 170 | indent () 171 | ch e 172 | 173 | let rec impl j = 174 | match j with 175 | | JsonNull -> str Tokens.Null 176 | | JsonBoolean true -> str Tokens.True 177 | | JsonBoolean false -> str Tokens.False 178 | | JsonNumber n -> num n 179 | | JsonString s -> estr s 180 | | JsonArray vs -> values '[' ']' vs impl 181 | | JsonObject ms -> values '{' '}' ms implkv 182 | and implkv (k,v) = 183 | estr k 184 | ch ':' 185 | newline () 186 | inc () 187 | indent () 188 | impl v 189 | dec () 190 | 191 | let json = 192 | match x with 193 | | JsonNull 194 | | JsonBoolean _ 195 | | JsonNumber _ 196 | | JsonString _ -> JsonArray [|x|] // In order to be valid JSON 197 | | JsonArray _ 198 | | JsonObject _ -> x 199 | 200 | impl json 201 | 202 | sb.ToString () 203 | 204 | /// Converts a JSON document into a string 205 | override x.ToString () : string = 206 | x.ToString false 207 | 208 | /// Converts a JSON document into a string 209 | /// @doIndent : True to indent 210 | /// @json : The JSON document 211 | let toString doIndent (json : Json) : string = 212 | json.ToString doIndent 213 | 214 | /// IParseVisitor is implemented by users wanting to parse 215 | /// a JSON document into a data structure other than MiniJson.Json 216 | type IParseVisitor = 217 | interface 218 | abstract NullValue : unit -> bool 219 | abstract BoolValue : bool -> bool 220 | abstract NumberValue : float -> bool 221 | abstract StringValue : StringBuilder -> bool 222 | abstract ArrayBegin : unit -> bool 223 | abstract ArrayEnd : unit -> bool 224 | abstract ObjectBegin : unit -> bool 225 | abstract ObjectEnd : unit -> bool 226 | abstract MemberKey : StringBuilder -> bool 227 | abstract ExpectedChar : int*char -> unit 228 | abstract Expected : int*string -> unit 229 | abstract Unexpected : int*string -> unit 230 | end 231 | 232 | module internal ParserDetails = 233 | [] 234 | let DefaultSize = 16 235 | 236 | [] 237 | let ErrorPrelude = "Failed to parse input as JSON" 238 | 239 | // Min & Max Exponent of float (double) 240 | // https://en.wikipedia.org/wiki/Double-precision_floating-point_format 241 | 242 | [] 243 | let MinimumPow10 = -323 // Min Exponent is -1022 (binary) but since double supports subnormals effective is even lower 244 | 245 | [] 246 | let MaximumPow10 = 308 // 1023 (binary) 247 | 248 | let Pow10Table = 249 | [| 250 | for i in MinimumPow10..MaximumPow10 -> pown 10. i 251 | |] 252 | 253 | let pow10 (n : int) : float = 254 | if n < MinimumPow10 then 0. 255 | elif n > MaximumPow10 then Double.PositiveInfinity 256 | else Pow10Table.[n - MinimumPow10] 257 | 258 | let inline isWhiteSpace (c : char) : bool = 259 | c = ' ' || c = '\t' || c = '\n' || c = '\r' 260 | 261 | let inline isDigit (c : char) : bool = 262 | c >= '0' && c <= '9' 263 | 264 | type IParseVisitor with 265 | member x.ExpectedChars (p : int, chars : string) : unit = 266 | let e = chars.Length - 1 267 | for i = 0 to e do 268 | x.ExpectedChar (p, chars.[i]) 269 | 270 | type JsonParser(s : string, v : IParseVisitor) = 271 | // TODO: Test replacing StringBuilder with a leaner datatype (array?) for performance 272 | let sb = StringBuilder DefaultSize 273 | let mutable pos = 0 274 | 275 | member x.position :int = pos 276 | 277 | // COVERAGE: inline makes code coverage not work 278 | #if DEBUG 279 | member x.neos : bool = pos < s.Length 280 | member x.eos : bool = pos >= s.Length 281 | member x.ch : char = s.[pos] 282 | member x.adv () : unit = pos <- pos + 1 283 | #else 284 | member inline x.neos : bool = pos < s.Length 285 | member inline x.eos : bool = pos >= s.Length 286 | member inline x.ch : char = s.[pos] 287 | member inline x.adv () : unit = pos <- pos + 1 288 | #endif 289 | 290 | member x.raise_Eos () : bool = 291 | v.Unexpected (pos, Tokens.EOS) 292 | false 293 | 294 | member x.raise_Value () : bool = 295 | v.Expected (pos, Tokens.Null ) 296 | v.Expected (pos, Tokens.True ) 297 | v.Expected (pos, Tokens.False) 298 | v.Expected (pos, Tokens.Digit) 299 | v.ExpectedChars (pos, Tokens.ValuePreludes) 300 | false 301 | 302 | member x.raise_RootValue () : bool = 303 | v.ExpectedChars (pos, Tokens.RootValuePreludes) 304 | false 305 | 306 | member x.raise_Char () : bool = 307 | v.Expected (pos, Tokens.Char) 308 | false 309 | 310 | member x.raise_Digit () : bool = 311 | v.Expected (pos, Tokens.Digit) 312 | false 313 | 314 | member x.raise_HexDigit () : bool = 315 | v.Expected (pos, Tokens.HexDigit) 316 | false 317 | 318 | member x.raise_Escapes () : bool = 319 | v.ExpectedChars (pos, Tokens.Escapes) 320 | false 321 | 322 | // COVERAGE: inline makes code coverage not work 323 | #if DEBUG 324 | member x.consume_WhiteSpace () : bool = 325 | #else 326 | member inline x.consume_WhiteSpace () : bool = 327 | #endif 328 | let l = s.Length 329 | while pos < l && (isWhiteSpace x.ch) do 330 | x.adv () 331 | true 332 | 333 | // COVERAGE: inline makes code coverage not work 334 | #if DEBUG 335 | member x.test_Char (c : char) : bool = 336 | #else 337 | member inline x.test_Char (c : char) : bool = 338 | #endif 339 | x.neos && x.ch = c 340 | 341 | // COVERAGE: inline makes code coverage not work 342 | #if DEBUG 343 | member x.tryConsume_Char (c : char) : bool = 344 | #else 345 | member inline x.tryConsume_Char (c : char) : bool = 346 | #endif 347 | if x.eos then 348 | v.ExpectedChar (pos, c) 349 | x.raise_Eos () 350 | elif x.ch = c then 351 | x.adv () 352 | true 353 | else 354 | v.ExpectedChar (pos, c) 355 | false 356 | 357 | // inline causes DEBUG mode to crash (because F# creates tuples of pointers) 358 | // COVERAGE: inline makes code coverage not work 359 | #if DEBUG 360 | member x.tryParse_AnyOf2 (first : char, second : char, r : char byref) : bool = 361 | #else 362 | member inline x.tryParse_AnyOf2 (first : char, second : char, r : char byref) : bool = 363 | #endif 364 | if x.eos then 365 | v.ExpectedChar (pos, first) 366 | v.ExpectedChar (pos, second) 367 | x.raise_Eos () 368 | else 369 | let c = x.ch 370 | if c = first || c = second then 371 | r <- c 372 | x.adv () 373 | true 374 | else 375 | v.ExpectedChar (pos, first) 376 | v.ExpectedChar (pos, second) 377 | false 378 | 379 | // COVERAGE: inline makes code coverage not work 380 | #if DEBUG 381 | member x.tryConsume_Token (tk : string) : bool = 382 | #else 383 | member inline x.tryConsume_Token (tk : string) : bool = 384 | #endif 385 | let tkl = tk.Length 386 | 387 | if tkl + pos <= s.Length then 388 | let spos = pos 389 | let mutable tpos = 0 390 | 391 | while tpos < tkl && tk.[tpos] = x.ch do 392 | tpos <- tpos + 1 393 | x.adv () 394 | 395 | if tpos = tkl then true 396 | else 397 | // To support error reporting, move back on failure 398 | pos <- spos 399 | false 400 | else 401 | false 402 | 403 | member x.tryParse_Null () : bool = 404 | if x.tryConsume_Token Tokens.Null then 405 | v.NullValue () 406 | else 407 | x.raise_Value () 408 | 409 | member x.tryParse_True () : bool = 410 | if x.tryConsume_Token Tokens.True then 411 | v.BoolValue true 412 | else 413 | x.raise_Value () 414 | 415 | member x.tryParse_False () : bool = 416 | if x.tryConsume_Token Tokens.False then 417 | v.BoolValue false 418 | else 419 | x.raise_Value () 420 | 421 | member x.tryParse_UInt (first : bool, r : float byref) : bool = 422 | let z = float '0' 423 | if x.eos then x.raise_Digit () || x.raise_Eos () || not first 424 | else 425 | let c = x.ch 426 | if isDigit c then 427 | x.adv () 428 | r <- 10.0*r + (float c - z) 429 | x.tryParse_UInt (false, &r) 430 | else 431 | x.raise_Digit () || not first 432 | 433 | member x.tryParse_UInt0 (r : float byref) : bool = 434 | // tryParse_UInt0 only consumes 0 if input is 0123, this in order to be conformant with spec 435 | let zero = x.tryConsume_Char '0' 436 | 437 | if zero then 438 | r <- 0.0 439 | true 440 | else 441 | x.tryParse_UInt (true, &r) 442 | 443 | member x.tryParse_Fraction (r : float byref) : bool = 444 | if x.tryConsume_Char '.' then 445 | let spos = pos 446 | let mutable uf = 0.0 447 | if x.tryParse_UInt (true, &uf) then 448 | r <- uf * (pow10 (spos - pos)) 449 | true 450 | else 451 | false 452 | else 453 | true // Fraction is optional 454 | 455 | member x.tryParse_Exponent (r : int byref) : bool = 456 | let mutable exp = ' ' 457 | if x.tryParse_AnyOf2 ('e', 'E', &exp) then 458 | let mutable sign = '+' 459 | // Ignore as sign is optional 460 | ignore <| x.tryParse_AnyOf2 ('+', '-', &sign) 461 | // TODO: Parsing exponent as float seems unnecessary 462 | let mutable ue = 0.0 463 | if x.tryParse_UInt (true, &ue) then 464 | let ur = int ue 465 | r <- if sign = '-' then -ur else ur 466 | true 467 | else 468 | false 469 | else 470 | true // Fraction is optional 471 | 472 | member x.tryParse_Number () : bool = 473 | let hasSign = x.tryConsume_Char '-' 474 | 475 | let mutable i = 0.0 476 | let mutable f = 0.0 477 | let mutable e = 0 478 | 479 | let result = 480 | x.tryParse_UInt0 (&i) 481 | && x.tryParse_Fraction (&f) 482 | && x.tryParse_Exponent (&e) 483 | 484 | if result then 485 | let uu = i + f 486 | let ff = if hasSign then -uu else uu 487 | let ee = pow10 e 488 | let rr = ff*ee 489 | v.NumberValue rr 490 | else 491 | false 492 | 493 | member x.tryParse_UnicodeChar (n : int, r : int) : bool = 494 | if n = 0 then 495 | ignore <| sb.Append (char r) 496 | true 497 | elif x.eos then x.raise_HexDigit () || x.raise_Eos () 498 | else 499 | let sr = r <<< 4 500 | let c = x.ch 501 | if isDigit c then x.adv () ; x.tryParse_UnicodeChar (n - 1, sr + (int c - int '0')) 502 | elif c >= 'A' && c <= 'F' then x.adv () ; x.tryParse_UnicodeChar (n - 1, sr + (int c - int 'A' + 10)) 503 | elif c >= 'a' && c <= 'f' then x.adv () ; x.tryParse_UnicodeChar (n - 1, sr + (int c - int 'a' + 10)) 504 | else 505 | x.raise_HexDigit () 506 | 507 | member x.tryParse_Chars (b : int) : bool = 508 | let inline app (c : char) = ignore <| sb.Append c 509 | let inline seq (e :int) = ignore <| sb.Append (s, b, e - b) 510 | 511 | if x.eos then x.raise_Char () || x.raise_Eos () 512 | else 513 | let c = x.ch 514 | match c with 515 | | '"' -> seq pos; true 516 | | '\r' | '\n' -> v.Unexpected (pos, Tokens.NewLine); false 517 | | '\\' -> 518 | seq pos 519 | x.adv () 520 | if x.eos then x.raise_Escapes () || x.raise_Eos () 521 | else 522 | let e = x.ch 523 | let result = 524 | match e with 525 | | '"' 526 | | '\\' 527 | | '/' -> app e ; x.adv (); true 528 | | 'b' -> app '\b' ; x.adv (); true 529 | | 'f' -> app '\f' ; x.adv (); true 530 | | 'n' -> app '\n' ; x.adv (); true 531 | | 'r' -> app '\r' ; x.adv (); true 532 | | 't' -> app '\t' ; x.adv (); true 533 | | 'u' -> 534 | x.adv () 535 | x.tryParse_UnicodeChar (4, 0) 536 | | _ -> x.raise_Escapes () 537 | result && x.tryParse_Chars pos 538 | | _ -> 539 | x.adv () 540 | x.tryParse_Chars b 541 | 542 | member x.tryParse_ToStringBuilder () : bool = 543 | ignore <| sb.Clear () 544 | x.tryConsume_Char '"' 545 | && x.tryParse_Chars pos 546 | && x.tryConsume_Char '"' 547 | 548 | member x.tryParse_String () : bool = 549 | x.tryParse_ToStringBuilder () 550 | && v.StringValue sb 551 | 552 | member x.tryParse_MemberKey () : bool = 553 | x.tryParse_ToStringBuilder () 554 | && v.MemberKey sb 555 | 556 | // COVERAGE: inline makes code coverage not work 557 | #if DEBUG 558 | member x.tryConsume_Delimiter (first : bool) : bool = 559 | #else 560 | member inline x.tryConsume_Delimiter (first : bool) : bool = 561 | #endif 562 | first || (x.tryConsume_Char ',' && x.consume_WhiteSpace ()) 563 | 564 | member x.tryParse_ArrayValues (first : bool) : bool = 565 | if x.test_Char ']' then 566 | true 567 | else 568 | x.tryConsume_Delimiter first 569 | && x.tryParse_Value () 570 | && x.tryParse_ArrayValues false 571 | 572 | member x.tryParse_Array () : bool = 573 | x.tryConsume_Char '[' 574 | && x.consume_WhiteSpace () 575 | && v.ArrayBegin () 576 | && x.tryParse_ArrayValues true 577 | && x.tryConsume_Char ']' 578 | && v.ArrayEnd () 579 | 580 | member x.tryParse_ObjectMembers (first : bool) : bool = 581 | if x.test_Char '}' then 582 | true 583 | else 584 | x.tryConsume_Delimiter first 585 | && x.tryParse_MemberKey () 586 | && x.consume_WhiteSpace () 587 | && x.tryConsume_Char ':' 588 | && x.consume_WhiteSpace () 589 | && x.tryParse_Value () 590 | && x.tryParse_ObjectMembers false 591 | 592 | member x.tryParse_Object () : bool = 593 | x.tryConsume_Char '{' 594 | && x.consume_WhiteSpace () 595 | && v.ObjectBegin () 596 | && x.tryParse_ObjectMembers true 597 | && x.tryConsume_Char '}' 598 | && v.ObjectEnd () 599 | 600 | member x.tryParse_Value (): bool = 601 | if x.eos then x.raise_Value () || x.raise_Eos () 602 | else 603 | let result = 604 | match x.ch with 605 | | 'n' -> x.tryParse_Null () 606 | | 't' -> x.tryParse_True () 607 | | 'f' -> x.tryParse_False () 608 | | '[' -> x.tryParse_Array () 609 | | '{' -> x.tryParse_Object () 610 | | '"' -> x.tryParse_String () 611 | | '-' -> x.tryParse_Number () 612 | | c when isDigit c -> x.tryParse_Number () 613 | | _ -> x.raise_Value () 614 | result && x.consume_WhiteSpace () 615 | 616 | member x.tryParse_RootValue () : bool = 617 | if x.eos then x.raise_RootValue () || x.raise_Eos () 618 | else 619 | let result = 620 | match x.ch with 621 | | '[' -> x.tryParse_Array () 622 | | '{' -> x.tryParse_Object () 623 | | _ -> x.raise_RootValue () 624 | result && x.consume_WhiteSpace () 625 | 626 | member x.tryParse_Eos () : bool = 627 | x.eos || (v.Expected (pos, Tokens.EOS); false) 628 | 629 | [] 630 | [] 631 | [] 632 | type BaseJsonValueBuilder() = 633 | abstract AddValue : Json -> bool 634 | abstract SetKey : string -> bool 635 | abstract CreateValue : Stack*Stack -> Json 636 | 637 | let emptyString = "" 638 | let nullValue = JsonNull 639 | let trueValue = JsonBoolean true 640 | let falseValue = JsonBoolean false 641 | let inline boolValue b = if b then trueValue else falseValue 642 | 643 | [] 644 | [] 645 | [] 646 | type RootJsonValueBuilder() = 647 | inherit BaseJsonValueBuilder() 648 | 649 | let mutable root = nullValue 650 | 651 | override x.AddValue (json : Json) : bool = 652 | root <- json 653 | true 654 | override x.SetKey (key : string) : bool = 655 | Debug.Assert false 656 | true 657 | override x.CreateValue (freeArrayBuilders, freeObjectBuilders) : Json = 658 | let result = root 659 | root <- nullValue 660 | result 661 | 662 | [] 663 | [] 664 | [] 665 | type ArrayJsonValueBuilder() = 666 | inherit BaseJsonValueBuilder() 667 | 668 | let mutable values = ResizeArray DefaultSize 669 | 670 | override x.AddValue (json : Json) : bool = 671 | values.Add json 672 | true 673 | override x.SetKey (key : string) : bool = 674 | Debug.Assert false 675 | true 676 | override x.CreateValue (freeArrayBuilders, freeObjectBuilders) : Json = 677 | let result = JsonArray (values.ToArray ()) 678 | values.Clear () 679 | freeArrayBuilders.Push x 680 | result 681 | 682 | [] 683 | type ObjectJsonValueBuilder() = 684 | inherit BaseJsonValueBuilder() 685 | 686 | let mutable key = emptyString 687 | let mutable members = ResizeArray DefaultSize 688 | 689 | override x.AddValue (json : Json) : bool = 690 | members.Add (key, json) 691 | true 692 | override x.SetKey (k : string) : bool = 693 | key <- k 694 | true 695 | override x.CreateValue (freeArrayBuilders, freeObjectBuilders) : Json = 696 | let result = JsonObject (members.ToArray ()) 697 | key <- emptyString 698 | members.Clear () 699 | freeObjectBuilders.Push x 700 | result 701 | 702 | let inline setKey (context : Stack) (key : string) : bool = 703 | let v = context.Peek () 704 | v.SetKey key 705 | 706 | let inline addValue (context : Stack) (json : Json) : bool = 707 | let v = context.Peek () 708 | v.AddValue json 709 | 710 | let inline popContext (context : Stack) freeArrayBuilders freeObjectBuilders : Json = 711 | let v = context.Pop () 712 | v.CreateValue (freeArrayBuilders, freeObjectBuilders) 713 | 714 | [] 715 | [] 716 | [] 717 | type JsonParseVisitor() = 718 | let context = Stack DefaultSize 719 | let freeArrayBuilders = Stack DefaultSize 720 | let freeObjectBuilders = Stack DefaultSize 721 | 722 | do 723 | context.Push (RootJsonValueBuilder ()) 724 | 725 | interface IParseVisitor with 726 | override x.NullValue () = addValue context <| nullValue 727 | override x.BoolValue v = addValue context <| boolValue v 728 | override x.NumberValue v = addValue context <| JsonNumber v 729 | override x.StringValue v = addValue context <| JsonString (v.ToString ()) 730 | 731 | override x.ArrayBegin () = 732 | if freeArrayBuilders.Count > 0 then 733 | context.Push (freeArrayBuilders.Pop ()) 734 | else 735 | context.Push (ArrayJsonValueBuilder ()) 736 | true 737 | override x.ArrayEnd () = addValue context <| popContext context freeArrayBuilders freeObjectBuilders 738 | override x.ObjectBegin () = 739 | if freeObjectBuilders.Count > 0 then 740 | context.Push (freeObjectBuilders.Pop ()) 741 | else 742 | context.Push (ObjectJsonValueBuilder ()) 743 | true 744 | override x.ObjectEnd () = addValue context <| popContext context freeArrayBuilders freeObjectBuilders 745 | override x.MemberKey v = setKey context <| v.ToString () 746 | 747 | override x.ExpectedChar (p,e) = () 748 | override x.Expected (p,e) = () 749 | override x.Unexpected (p,ue) = () 750 | 751 | member x.Root () = 752 | Debug.Assert (context.Count = 1) 753 | popContext context freeArrayBuilders freeObjectBuilders 754 | 755 | [] 756 | [] 757 | [] 758 | type JsonErrorParseVisitor(epos : int) = 759 | let expectedChars = ResizeArray DefaultSize 760 | let expected = ResizeArray DefaultSize 761 | let unexpected = ResizeArray DefaultSize 762 | 763 | let filter f = f |> Seq.sort |> Seq.distinct |> Seq.toArray 764 | 765 | interface IParseVisitor with 766 | override x.NullValue () = true 767 | override x.BoolValue v = true 768 | override x.NumberValue v = true 769 | override x.StringValue v = true 770 | 771 | override x.ArrayBegin () = true 772 | override x.ArrayEnd () = true 773 | override x.ObjectBegin () = true 774 | override x.ObjectEnd () = true 775 | override x.MemberKey v = true 776 | 777 | override x.ExpectedChar (p,e) = if p = epos then expectedChars.Add e 778 | override x.Expected (p,e) = if p = epos then expected.Add e 779 | override x.Unexpected (p,ue) = if p = epos then unexpected.Add ue 780 | 781 | member x.ExpectedChars = filter expectedChars 782 | member x.Expected = filter expected 783 | member x.Unexpected = filter unexpected 784 | 785 | open ParserDetails 786 | 787 | /// Attempts to parse a JSON document from a string 788 | /// visitor : Parser visitor object 789 | /// input : Input string 790 | let tryParse (visitor : IParseVisitor) (input : string) (pos : int byref) : bool = 791 | let jp = JsonParser (input, visitor) 792 | let result = 793 | jp.consume_WhiteSpace () 794 | && jp.tryParse_RootValue () 795 | && jp.tryParse_Eos () 796 | 797 | pos <- jp.position 798 | 799 | result 800 | 801 | /// Returned by parse function 802 | type ParseResult = 803 | /// (json) - Holds the parsed JSON document 804 | | Success of Json 805 | /// (message, pos) - Holds the error description and position of failure 806 | | Failure of string*int 807 | 808 | /// Attempts to parse a JSON document from a string 809 | /// @fullErrorInfo : True to generate full errorinfo 810 | /// False only shows position (faster) 811 | /// @input : Input string 812 | let parse (fullErrorInfo : bool) (input : string) : ParseResult = 813 | let mutable pos = 0 814 | let v = JsonParseVisitor () 815 | 816 | match tryParse (upcast v) input &pos, fullErrorInfo with 817 | | true , _ -> 818 | Success (v.Root ()) 819 | | false , false -> 820 | Failure (ErrorPrelude, pos) 821 | | false , true -> 822 | let mutable epos = 0 823 | let ev = JsonErrorParseVisitor (pos) 824 | 825 | ignore <| tryParse (upcast ev) input &epos 826 | 827 | let sb = StringBuilder () 828 | let inline str (s : string) = ignore <| sb.Append s 829 | let inline strl (s : string) = ignore <| sb.AppendLine s 830 | let inline ch (c : char) = ignore <| sb.Append c 831 | let inline line () = ignore <| sb.AppendLine () 832 | 833 | let e = 834 | Seq.concat 835 | [| 836 | ev.ExpectedChars |> Seq.map (fun c -> "'" + (c.ToString ()) + "'") 837 | upcast ev.Expected 838 | |] 839 | |> Seq.toArray 840 | let ue = ev.Unexpected 841 | 842 | let values prefix (vs : string []) = 843 | if vs.Length = 0 then () 844 | else 845 | line () 846 | str prefix 847 | let e = vs.Length - 1 848 | for i = 0 to e do 849 | let v = vs.[i] 850 | if i = 0 then () 851 | elif i = e then str " or " 852 | else str ", " 853 | str v 854 | 855 | let windowSize = 60 856 | let windowBegin,windowEnd,windowPos = 857 | if input.Length < windowSize then 858 | 0, input.Length - 1, pos 859 | else 860 | let hs = windowSize / 2 861 | let b = pos - hs 862 | let e = pos + hs 863 | let ab = max 0 b 864 | let ae = min (input.Length - 1) (e + ab - b) 865 | let ap = pos - ab 866 | ab, ae, ap 867 | 868 | strl ErrorPrelude 869 | for i = windowBegin to windowEnd do 870 | let c = 871 | match input.[i] with 872 | | '\n' 873 | | '\r' -> ' ' 874 | | c -> c 875 | ch c 876 | line () 877 | ignore <| sb.Append ('-', windowPos) 878 | str "^ Pos: " 879 | ignore <| sb.Append pos 880 | values "Expected: " e 881 | values "Unexpected: " ue 882 | 883 | Failure (sb.ToString (), pos) 884 | -------------------------------------------------------------------------------- /src/MiniJson/MiniJson.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 68c7233e-b616-4233-a3b8-6881f11dfb2f 9 | Library 10 | MiniJson 11 | MiniJson 12 | v4.5 13 | true 14 | 4.4.0.0 15 | MiniJson 16 | 17 | 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | false 26 | bin\Debug\ 27 | TRACE;DEBUG;PUBLIC_MINIJSON 28 | 5 29 | AnyCPU 30 | bin\Debug\MiniJson.XML 31 | true 32 | 33 | 34 | pdbonly 35 | true 36 | true 37 | bin\Release\ 38 | TRACE;PUBLIC_MINIJSON 39 | 5 40 | AnyCPU 41 | bin\Release\MiniJson.XML 42 | true 43 | 44 | 45 | 11 46 | 47 | 48 | true 49 | full 50 | false 51 | false 52 | bin\Debug\ 53 | TRACE;DEBUG;PUBLIC_MINIJSON 54 | 5 55 | bin\Debug\MiniJson.XML 56 | true 57 | x64 58 | 59 | 60 | pdbonly 61 | true 62 | true 63 | bin\Release\ 64 | TRACE;PUBLIC_MINIJSON 65 | 5 66 | bin\Release\MiniJson.XML 67 | true 68 | x64 69 | 70 | 71 | true 72 | full 73 | false 74 | false 75 | bin\Debug\ 76 | TRACE;DEBUG;PUBLIC_MINIJSON 77 | 5 78 | bin\Debug\MiniJson.XML 79 | true 80 | x86 81 | 82 | 83 | pdbonly 84 | true 85 | true 86 | bin\Release\ 87 | TRACE;PUBLIC_MINIJSON 88 | 5 89 | bin\Release\MiniJson.XML 90 | true 91 | x86 92 | 93 | 94 | 95 | 96 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 97 | 98 | 99 | 100 | 101 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 102 | 103 | 104 | 105 | 106 | 107 | 108 | Global_AssemblyInfo.fs 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | FSharp.Core 121 | FSharp.Core.dll 122 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 131 | 132 | 133 | 140 | -------------------------------------------------------------------------------- /src/MiniJson/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------