├── .gitignore ├── COPYING ├── Docs ├── Makefile ├── out │ ├── css │ │ ├── docs.css │ │ └── vendor │ │ │ ├── normalize.css │ │ │ └── unsemantic.css │ └── quickstart.html ├── quickstart │ ├── guide.md │ └── intro.md ├── template-web.htm └── template.htm ├── Libraries └── nunit.framework.dll ├── LitJson.csproj ├── NEWS.md ├── README.md ├── Source ├── Extensions.cs ├── IJsonWrapper.cs ├── JsonAttribute.cs ├── JsonData.cs ├── JsonException.cs ├── JsonMapper.cs ├── JsonMockWrapper.cs ├── JsonReader.cs ├── JsonWriter.cs ├── Lexer.cs ├── ParserToken.cs ├── Platform.cs └── Unity │ └── UnityTypeBindings.cs ├── UnityLitJson.unitypackage ├── benchmarks ├── .gitignore ├── Benchmark.cs ├── BmCommon.cs ├── BmJayrockExport.cs ├── BmJayrockImport.cs ├── BmJayrockOutput.cs ├── BmJayrockReader.cs ├── BmJayrockWriter.cs ├── BmLitJsonExport.cs ├── BmLitJsonImport.cs ├── BmLitJsonOutput.cs ├── BmLitJsonReader.cs ├── BmLitJsonWriter.cs ├── BmNewtonsoftExport.cs ├── BmNewtonsoftImport.cs ├── BmNewtonsoftOutput.cs ├── BmNewtonsoftReader.cs ├── BmNewtonsoftWriter.cs ├── GNUmakefile └── README ├── build └── vs2012 │ ├── LitJson.Tests.csproj │ ├── LitJson.csproj │ └── LitJson.sln └── test ├── JsonAliasTest.cs ├── JsonArrayImporterTest.cs ├── JsonDataTest.cs ├── JsonIgnoreMemberTest.cs ├── JsonIgnoreTest.cs ├── JsonMapperTest.cs ├── JsonObjectImporterTest.cs ├── JsonReaderTest.cs ├── JsonRoundTripTest.cs ├── JsonTypeHintingTest.cs ├── JsonWriterTest.cs └── json-example.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /src/LitJson/AssemblyInfo.cs 2 | /website 3 | 4 | /build/vs2012/bin 5 | /build/vs2012/obj 6 | /build/vs2012/*.suo 7 | TestResult.xml 8 | *.sublime-project 9 | *.sublime-workspace 10 | *.mdb 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | Thank you for reading this notice. Following the tradition of other public 27 | domain projects, here's a blessing: 28 | 29 | May you find forgiveness for yourself and forgive others. 30 | May you experience and share the gift of unconditional love. 31 | May you see light, wherever the illusion of darkness appears. 32 | -------------------------------------------------------------------------------- /Docs/Makefile: -------------------------------------------------------------------------------- 1 | website_dir = $(CURDIR)/../website 2 | 3 | PANDOC_OPTS = \ 4 | -S -s --toc --toc-depth=2 -t html5 \ 5 | --css="css/docs.css" 6 | 7 | 8 | ## 9 | ## Quick Start Guide 10 | ## 11 | QUICKSTART_OUT = out/quickstart.html 12 | QUICKSTART_FILES = \ 13 | quickstart/intro.md \ 14 | quickstart/guide.md 15 | 16 | 17 | ## 18 | ## Rules 19 | ## 20 | all: api quickstart 21 | 22 | api: 23 | 24 | quickstart: $(QUICKSTART_OUT) 25 | 26 | $(QUICKSTART_OUT): $(QUICKSTART_FILES) template.htm 27 | pandoc $(PANDOC_OPTS) --template=template.htm \ 28 | -V title="LitJSON Quickstart Guide" \ 29 | -V pagetitle="LitJSON Quickstart Guide" \ 30 | -o $(QUICKSTART_OUT) $(QUICKSTART_FILES) 31 | 32 | website: 33 | mkdir -p $(website_dir)/out/docs 34 | cp -f out/css/docs.css $(website_dir)/out/css/ 35 | pandoc $(PANDOC_OPTS) --template=template-web.htm \ 36 | -V title="LitJSON Quickstart Guide" \ 37 | -V pagetitle="LitJSON Quickstart Guide" \ 38 | -o $(website_dir)/out/docs/quickstart.html $(QUICKSTART_FILES) 39 | 40 | clean: 41 | rm -rf $(QUICKSTART_OUT) 42 | 43 | .PHONY: all api clean quickstart website 44 | -------------------------------------------------------------------------------- /Docs/out/css/docs.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 0.8em; 3 | } 4 | 5 | pre.sourceCode, code.sourceCode { 6 | background-color: #eeeeec; 7 | } 8 | pre.sourceCode { 9 | padding: 16px; 10 | min-width: 50em; 11 | width: auto; 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid #babdb6; 15 | } 16 | pre.console { 17 | padding: 16px; 18 | min-width: 50em; 19 | width: auto; 20 | display: inline-block; 21 | border-radius: 8px; 22 | color: #ffffff; 23 | background-color: #2e3436; 24 | border: 1px solid #000000; 25 | } 26 | 27 | h1.title { 28 | color: #204a87; 29 | } 30 | 31 | h1 a, h2 a, h3 a, h4 a { 32 | color: #3465a4; 33 | text-decoration: none; 34 | } 35 | h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover { 36 | text-decoration: underline; 37 | } 38 | -------------------------------------------------------------------------------- /Docs/out/css/vendor/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address styling not present in IE 8/9. 48 | */ 49 | 50 | [hidden] { 51 | display: none; 52 | } 53 | 54 | /* ========================================================================== 55 | Base 56 | ========================================================================== */ 57 | 58 | /** 59 | * 1. Prevent system color scheme's background color being used in Firefox, IE, 60 | * and Opera. 61 | * 2. Prevent system color scheme's text color being used in Firefox, IE, and 62 | * Opera. 63 | * 3. Set default font family to sans-serif. 64 | * 4. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | background: #fff; /* 1 */ 70 | color: #000; /* 2 */ 71 | font-family: sans-serif; /* 3 */ 72 | -ms-text-size-adjust: 100%; /* 4 */ 73 | -webkit-text-size-adjust: 100%; /* 4 */ 74 | } 75 | 76 | /** 77 | * Remove default margin. 78 | */ 79 | 80 | body { 81 | margin: 0; 82 | } 83 | 84 | /* ========================================================================== 85 | Links 86 | ========================================================================== */ 87 | 88 | /** 89 | * Address `outline` inconsistency between Chrome and other browsers. 90 | */ 91 | 92 | a:focus { 93 | outline: thin dotted; 94 | } 95 | 96 | /** 97 | * Improve readability when focused and also mouse hovered in all browsers. 98 | */ 99 | 100 | a:active, 101 | a:hover { 102 | outline: 0; 103 | } 104 | 105 | /* ========================================================================== 106 | Typography 107 | ========================================================================== */ 108 | 109 | /** 110 | * Address variable `h1` font-size and margin within `section` and `article` 111 | * contexts in Firefox 4+, Safari 5, and Chrome. 112 | */ 113 | 114 | h1 { 115 | font-size: 2em; 116 | margin: 0.67em 0; 117 | } 118 | 119 | /** 120 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 121 | */ 122 | 123 | abbr[title] { 124 | border-bottom: 1px dotted; 125 | } 126 | 127 | /** 128 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 129 | */ 130 | 131 | b, 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | /** 137 | * Address styling not present in Safari 5 and Chrome. 138 | */ 139 | 140 | dfn { 141 | font-style: italic; 142 | } 143 | 144 | /** 145 | * Address differences between Firefox and other browsers. 146 | */ 147 | 148 | hr { 149 | -moz-box-sizing: content-box; 150 | box-sizing: content-box; 151 | height: 0; 152 | } 153 | 154 | /** 155 | * Address styling not present in IE 8/9. 156 | */ 157 | 158 | mark { 159 | background: #ff0; 160 | color: #000; 161 | } 162 | 163 | /** 164 | * Correct font family set oddly in Safari 5 and Chrome. 165 | */ 166 | 167 | code, 168 | kbd, 169 | pre, 170 | samp { 171 | font-family: monospace, serif; 172 | font-size: 1em; 173 | } 174 | 175 | /** 176 | * Improve readability of pre-formatted text in all browsers. 177 | */ 178 | 179 | pre { 180 | white-space: pre-wrap; 181 | } 182 | 183 | /** 184 | * Set consistent quote types. 185 | */ 186 | 187 | q { 188 | quotes: "\201C" "\201D" "\2018" "\2019"; 189 | } 190 | 191 | /** 192 | * Address inconsistent and variable font size in all browsers. 193 | */ 194 | 195 | small { 196 | font-size: 80%; 197 | } 198 | 199 | /** 200 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 201 | */ 202 | 203 | sub, 204 | sup { 205 | font-size: 75%; 206 | line-height: 0; 207 | position: relative; 208 | vertical-align: baseline; 209 | } 210 | 211 | sup { 212 | top: -0.5em; 213 | } 214 | 215 | sub { 216 | bottom: -0.25em; 217 | } 218 | 219 | /* ========================================================================== 220 | Embedded content 221 | ========================================================================== */ 222 | 223 | /** 224 | * Remove border when inside `a` element in IE 8/9. 225 | */ 226 | 227 | img { 228 | border: 0; 229 | } 230 | 231 | /** 232 | * Correct overflow displayed oddly in IE 9. 233 | */ 234 | 235 | svg:not(:root) { 236 | overflow: hidden; 237 | } 238 | 239 | /* ========================================================================== 240 | Figures 241 | ========================================================================== */ 242 | 243 | /** 244 | * Address margin not present in IE 8/9 and Safari 5. 245 | */ 246 | 247 | figure { 248 | margin: 0; 249 | } 250 | 251 | /* ========================================================================== 252 | Forms 253 | ========================================================================== */ 254 | 255 | /** 256 | * Define consistent border, margin, and padding. 257 | */ 258 | 259 | fieldset { 260 | border: 1px solid #c0c0c0; 261 | margin: 0 2px; 262 | padding: 0.35em 0.625em 0.75em; 263 | } 264 | 265 | /** 266 | * 1. Correct `color` not being inherited in IE 8/9. 267 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 268 | */ 269 | 270 | legend { 271 | border: 0; /* 1 */ 272 | padding: 0; /* 2 */ 273 | } 274 | 275 | /** 276 | * 1. Correct font family not being inherited in all browsers. 277 | * 2. Correct font size not being inherited in all browsers. 278 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 279 | */ 280 | 281 | button, 282 | input, 283 | select, 284 | textarea { 285 | font-family: inherit; /* 1 */ 286 | font-size: 100%; /* 2 */ 287 | margin: 0; /* 3 */ 288 | } 289 | 290 | /** 291 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 292 | * the UA stylesheet. 293 | */ 294 | 295 | button, 296 | input { 297 | line-height: normal; 298 | } 299 | 300 | /** 301 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 302 | * All other form control elements do not inherit `text-transform` values. 303 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 304 | * Correct `select` style inheritance in Firefox 4+ and Opera. 305 | */ 306 | 307 | button, 308 | select { 309 | text-transform: none; 310 | } 311 | 312 | /** 313 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 314 | * and `video` controls. 315 | * 2. Correct inability to style clickable `input` types in iOS. 316 | * 3. Improve usability and consistency of cursor style between image-type 317 | * `input` and others. 318 | */ 319 | 320 | button, 321 | html input[type="button"], /* 1 */ 322 | input[type="reset"], 323 | input[type="submit"] { 324 | -webkit-appearance: button; /* 2 */ 325 | cursor: pointer; /* 3 */ 326 | } 327 | 328 | /** 329 | * Re-set default cursor for disabled elements. 330 | */ 331 | 332 | button[disabled], 333 | html input[disabled] { 334 | cursor: default; 335 | } 336 | 337 | /** 338 | * 1. Address box sizing set to `content-box` in IE 8/9. 339 | * 2. Remove excess padding in IE 8/9. 340 | */ 341 | 342 | input[type="checkbox"], 343 | input[type="radio"] { 344 | box-sizing: border-box; /* 1 */ 345 | padding: 0; /* 2 */ 346 | } 347 | 348 | /** 349 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 350 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 351 | * (include `-moz` to future-proof). 352 | */ 353 | 354 | input[type="search"] { 355 | -webkit-appearance: textfield; /* 1 */ 356 | -moz-box-sizing: content-box; 357 | -webkit-box-sizing: content-box; /* 2 */ 358 | box-sizing: content-box; 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari 5 and Chrome 363 | * on OS X. 364 | */ 365 | 366 | input[type="search"]::-webkit-search-cancel-button, 367 | input[type="search"]::-webkit-search-decoration { 368 | -webkit-appearance: none; 369 | } 370 | 371 | /** 372 | * Remove inner padding and border in Firefox 4+. 373 | */ 374 | 375 | button::-moz-focus-inner, 376 | input::-moz-focus-inner { 377 | border: 0; 378 | padding: 0; 379 | } 380 | 381 | /** 382 | * 1. Remove default vertical scrollbar in IE 8/9. 383 | * 2. Improve readability and alignment in all browsers. 384 | */ 385 | 386 | textarea { 387 | overflow: auto; /* 1 */ 388 | vertical-align: top; /* 2 */ 389 | } 390 | 391 | /* ========================================================================== 392 | Tables 393 | ========================================================================== */ 394 | 395 | /** 396 | * Remove most spacing between table cells. 397 | */ 398 | 399 | table { 400 | border-collapse: collapse; 401 | border-spacing: 0; 402 | } 403 | -------------------------------------------------------------------------------- /Docs/quickstart/guide.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | ## Mapping JSON to objects and vice versa 4 | 5 | In order to consume data in JSON format inside .Net programs, the natural 6 | approach that comes to mind is to use JSON text to populate a new instance 7 | of a particular class; either a custom one, built to match the structure of 8 | the input JSON text, or a more general one which acts as a dictionary. 9 | 10 | Conversely, in order to build new JSON strings from data stored in objects, 11 | a simple _export_--like operation sounds like a good idea. 12 | 13 | For this purpose, *LitJSON* includes the `JsonMapper` class, 14 | which provides two main methods used to do JSON--to--object and 15 | object--to--JSON conversions. These methods are 16 | `JsonMapper.ToObject` and `JsonMapper.ToJson`. 17 | 18 | ### Simple `JsonMapper` examples 19 | 20 | As the following example demonstrates, the `ToObject` method has a generic 21 | variant, `JsonMapper.ToObject`, that is used to specify the type of the 22 | object to be returned. 23 | 24 | ```cs 25 | using LitJson; 26 | using System; 27 | 28 | public class Person 29 | { 30 | // C# 3.0 auto-implemented properties 31 | public string Name { get; set; } 32 | public int Age { get; set; } 33 | public DateTime Birthday { get; set; } 34 | } 35 | 36 | public class JsonSample 37 | { 38 | public static void Main() 39 | { 40 | PersonToJson(); 41 | JsonToPerson(); 42 | } 43 | 44 | public static void PersonToJson() 45 | { 46 | Person bill = new Person(); 47 | 48 | bill.Name = "William Shakespeare"; 49 | bill.Age = 51; 50 | bill.Birthday = new DateTime(1564, 4, 26); 51 | 52 | string json_bill = JsonMapper.ToJson(bill); 53 | 54 | Console.WriteLine(json_bill); 55 | } 56 | 57 | public static void JsonToPerson() 58 | { 59 | string json = @" 60 | { 61 | ""Name"" : ""Thomas More"", 62 | ""Age"" : 57, 63 | ""Birthday"" : ""02/07/1478 00:00:00"" 64 | }"; 65 | 66 | Person thomas = JsonMapper.ToObject(json); 67 | 68 | Console.WriteLine("Thomas' age: {0}", thomas.Age); 69 | } 70 | } 71 | ``` 72 | 73 | Output from the example: 74 | 75 | ```console 76 | {"Name":"William Shakespeare","Age":51,"Birthday":"04/26/1564 00:00:00"} 77 | Thomas' age: 57 78 | ``` 79 | 80 | ### Using the non--generic variant of `JsonMapper.ToObject` 81 | 82 | When JSON data is to be read and a custom class that matches a particular 83 | data structure is not available or desired, users can use the non--generic 84 | variant of `ToObject`, which returns a `JsonData` instance. `JsonData` is a 85 | general purpose type that can hold any of the data types supported by JSON, 86 | including lists and dictionaries. 87 | 88 | ```cs 89 | using LitJson; 90 | using System; 91 | 92 | public class JsonSample 93 | { 94 | public static void Main() 95 | { 96 | string json = @" 97 | { 98 | ""album"" : { 99 | ""name"" : ""The Dark Side of the Moon"", 100 | ""artist"" : ""Pink Floyd"", 101 | ""year"" : 1973, 102 | ""tracks"" : [ 103 | ""Speak To Me"", 104 | ""Breathe"", 105 | ""On The Run"" 106 | ] 107 | } 108 | } 109 | "; 110 | 111 | LoadAlbumData(json); 112 | } 113 | 114 | public static void LoadAlbumData(string json_text) 115 | { 116 | Console.WriteLine("Reading data from the following JSON string: {0}", 117 | json_text); 118 | 119 | JsonData data = JsonMapper.ToObject(json_text); 120 | 121 | // Dictionaries are accessed like a hash-table 122 | Console.WriteLine("Album's name: {0}", data["album"]["name"]); 123 | 124 | // Scalar elements stored in a JsonData instance can be cast to 125 | // their natural types 126 | string artist = (string) data["album"]["artist"]; 127 | int year = (int) data["album"]["year"]; 128 | 129 | Console.WriteLine("Recorded by {0} in {1}", artist, year); 130 | 131 | // Arrays are accessed like regular lists as well 132 | Console.WriteLine("First track: {0}", data["album"]["tracks"][0]); 133 | } 134 | } 135 | ``` 136 | 137 | Output from the example: 138 | 139 | ```console 140 | Reading data from the following JSON string: 141 | { 142 | "album" : { 143 | "name" : "The Dark Side of the Moon", 144 | "artist" : "Pink Floyd", 145 | "year" : 1973, 146 | "tracks" : [ 147 | "Speak To Me", 148 | "Breathe", 149 | "On The Run" 150 | ] 151 | } 152 | } 153 | 154 | Album's name: The Dark Side of the Moon 155 | Recorded by Pink Floyd in 1973 156 | First track: Speak To Me 157 | ``` 158 | 159 | ## Readers and Writers 160 | 161 | An alternative interface to handling JSON data that might be familiar to 162 | some developers is through classes that make it possible to read and write 163 | data in a stream--like fashion. These classes are `JsonReader` and 164 | `JsonWriter`. 165 | 166 | These two types are in fact the foundation of this library, and the 167 | `JsonMapper` type is built on top of them, so in a way, the developer can 168 | think of the reader and writer classes as the low--level programming 169 | interface for LitJSON. 170 | 171 | ### Using `JsonReader` 172 | 173 | ```cs 174 | using LitJson; 175 | using System; 176 | 177 | public class DataReader 178 | { 179 | public static void Main() 180 | { 181 | string sample = @"{ 182 | ""name"" : ""Bill"", 183 | ""age"" : 32, 184 | ""awake"" : true, 185 | ""n"" : 1994.0226, 186 | ""note"" : [ ""life"", ""is"", ""but"", ""a"", ""dream"" ] 187 | }"; 188 | 189 | PrintJson(sample); 190 | } 191 | 192 | public static void PrintJson(string json) 193 | { 194 | JsonReader reader = new JsonReader(json); 195 | 196 | Console.WriteLine ("{0,14} {1,10} {2,16}", "Token", "Value", "Type"); 197 | Console.WriteLine (new String ('-', 42)); 198 | 199 | // The Read() method returns false when there's nothing else to read 200 | while (reader.Read()) { 201 | string type = reader.Value != null ? 202 | reader.Value.GetType().ToString() : ""; 203 | 204 | Console.WriteLine("{0,14} {1,10} {2,16}", 205 | reader.Token, reader.Value, type); 206 | } 207 | } 208 | } 209 | ``` 210 | 211 | This example would produce the following output: 212 | 213 | ```console 214 | Token Value Type 215 | ------------------------------------------ 216 | ObjectStart 217 | PropertyName name System.String 218 | String Bill System.String 219 | PropertyName age System.String 220 | Int 32 System.Int32 221 | PropertyName awake System.String 222 | Boolean True System.Boolean 223 | PropertyName n System.String 224 | Double 1994.0226 System.Double 225 | PropertyName note System.String 226 | ArrayStart 227 | String life System.String 228 | String is System.String 229 | String but System.String 230 | String a System.String 231 | String dream System.String 232 | ArrayEnd 233 | ObjectEnd 234 | ``` 235 | 236 | ### Using `JsonWriter` 237 | 238 | The `JsonWriter` class is quite simple. Keep in mind that if you want to 239 | convert some arbitrary object into a JSON string, you'd normally just use 240 | `JsonMapper.ToJson`. 241 | 242 | ```cs 243 | using LitJson; 244 | using System; 245 | using System.Text; 246 | 247 | public class DataWriter 248 | { 249 | public static void Main() 250 | { 251 | StringBuilder sb = new StringBuilder(); 252 | JsonWriter writer = new JsonWriter(sb); 253 | 254 | writer.WriteArrayStart(); 255 | writer.Write(1); 256 | writer.Write(2); 257 | writer.Write(3); 258 | 259 | writer.WriteObjectStart(); 260 | writer.WritePropertyName("color"); 261 | writer.Write("blue"); 262 | writer.WriteObjectEnd(); 263 | 264 | writer.WriteArrayEnd(); 265 | 266 | Console.WriteLine(sb.ToString()); 267 | } 268 | } 269 | ``` 270 | 271 | Output from the example: 272 | 273 | ```console 274 | [1,2,3,{"color":"blue"}] 275 | ``` 276 | 277 | ## Configuring the library's behaviour 278 | 279 | JSON is a very concise data--interchange format; nothing more, nothing less. 280 | For this reason, handling data in JSON format inside a program may require a 281 | deliberate decision on your part regarding some little detail that goes 282 | beyond the scope of JSON's specs. 283 | 284 | Consider, for example, reading data from JSON strings where 285 | single--quotes are used to delimit strings, or Javascript--style 286 | comments are included as a form of documentation. Those things are not part 287 | of the JSON standard, but they are commonly used by some developers, so you 288 | may want to be forgiving or strict depending on the situation. Or what about 289 | if you want to convert a *.Net* object into a JSON string, but 290 | pretty--printed (using indentation)? 291 | 292 | To declare the behaviour you want, you may change a few properties from your 293 | `JsonReader` and `JsonWriter` objects. 294 | 295 | ### Configuration of `JsonReader` 296 | 297 | ```cs 298 | using LitJson; 299 | using System; 300 | 301 | public class JsonReaderConfigExample 302 | { 303 | public static void Main() 304 | { 305 | string json; 306 | 307 | json = " /* these are some numbers */ [ 2, 3, 5, 7, 11 ] "; 308 | TestReadingArray(json); 309 | 310 | json = " [ \"hello\", 'world' ] "; 311 | TestReadingArray(json); 312 | } 313 | 314 | static void TestReadingArray(string json_array) 315 | { 316 | JsonReader defaultReader, customReader; 317 | 318 | defaultReader = new JsonReader(json_array); 319 | customReader = new JsonReader(json_array); 320 | 321 | customReader.AllowComments = false; 322 | customReader.AllowSingleQuotedStrings = false; 323 | 324 | ReadArray(defaultReader); 325 | ReadArray(customReader); 326 | } 327 | 328 | static void ReadArray(JsonReader reader) 329 | { 330 | Console.WriteLine("Reading an array"); 331 | 332 | try { 333 | JsonData data = JsonMapper.ToObject(reader); 334 | 335 | foreach (JsonData elem in data) 336 | Console.Write(" {0}", elem); 337 | 338 | Console.WriteLine(" [end]"); 339 | } 340 | catch (Exception e) { 341 | Console.WriteLine(" Exception caught: {0}", e.Message); 342 | } 343 | } 344 | } 345 | ``` 346 | 347 | The output would be: 348 | 349 | ```console 350 | Reading an array 351 | 2 3 5 7 11 [end] 352 | Reading an array 353 | Exception caught: Invalid character '/' in input string 354 | Reading an array 355 | hello world [end] 356 | Reading an array 357 | Exception caught: Invalid character ''' in input string 358 | ``` 359 | 360 | ### Configuration of `JsonWriter` 361 | 362 | ```cs 363 | using LitJson; 364 | using System; 365 | 366 | public enum AnimalType 367 | { 368 | Dog, 369 | Cat, 370 | Parrot 371 | } 372 | 373 | public class Animal 374 | { 375 | public string Name { get; set; } 376 | public AnimalType Type { get; set; } 377 | public int Age { get; set; } 378 | public string[] Toys { get; set; } 379 | } 380 | 381 | public class JsonWriterConfigExample 382 | { 383 | public static void Main() 384 | { 385 | var dog = new Animal { 386 | Name = "Noam Chompsky", 387 | Type = AnimalType.Dog, 388 | Age = 3, 389 | Toys = new string[] { "rubber bone", "tennis ball" } 390 | }; 391 | 392 | var cat = new Animal { 393 | Name = "Colonel Meow", 394 | Type = AnimalType.Cat, 395 | Age = 5, 396 | Toys = new string[] { "cardboard box" } 397 | }; 398 | 399 | TestWritingAnimal(dog); 400 | TestWritingAnimal(cat, 2); 401 | } 402 | 403 | static void TestWritingAnimal(Animal pet, int indentLevel = 0) 404 | { 405 | Console.WriteLine("\nConverting {0}'s data into JSON..", pet.Name); 406 | JsonWriter writer1 = new JsonWriter(Console.Out); 407 | JsonWriter writer2 = new JsonWriter(Console.Out); 408 | 409 | writer2.PrettyPrint = true; 410 | if (indentLevel != 0) 411 | writer2.IndentValue = indentLevel; 412 | 413 | Console.WriteLine("Default JSON string:"); 414 | JsonMapper.ToJson(pet, writer1); 415 | 416 | Console.Write("\nPretty-printed:"); 417 | JsonMapper.ToJson(pet, writer2); 418 | Console.WriteLine(""); 419 | } 420 | } 421 | ``` 422 | 423 | The output from this example is: 424 | 425 | ```console 426 | 427 | Converting Noam Chompsky's data into JSON.. 428 | Default JSON string: 429 | {"Name":"Noam Chompsky","Type":0,"Age":3,"Toys":["rubber bone","tennis ball"]} 430 | Pretty-printed: 431 | { 432 | "Name" : "Noam Chompsky", 433 | "Type" : 0, 434 | "Age" : 3, 435 | "Toys" : [ 436 | "rubber bone", 437 | "tennis ball" 438 | ] 439 | } 440 | 441 | Converting Colonel Meow's data into JSON.. 442 | Default JSON string: 443 | {"Name":"Colonel Meow","Type":1,"Age":5,"Toys":["cardboard box"]} 444 | Pretty-printed: 445 | { 446 | "Name" : "Colonel Meow", 447 | "Type" : 1, 448 | "Age" : 5, 449 | "Toys" : [ 450 | "cardboard box" 451 | ] 452 | } 453 | ``` 454 | -------------------------------------------------------------------------------- /Docs/quickstart/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | JSON is a simple, yet powerful notation to specify data. It defines simple 4 | scalar types such as *boolean*, *number* (integers and reals) and *string*, 5 | and a couple of data structures: arrays (lists) and objects (dictionaries). 6 | For more information on the JSON format, visit 7 | [JSON.org](http://www.json.org/). 8 | 9 | LitJSON is written in C#, and it's intended to be small, fast and easy to 10 | use. It was developed on a GNU/Linux environment, using the 11 | [Mono](http://www.mono-project.com/) framework. 12 | -------------------------------------------------------------------------------- /Docs/template-web.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | 16 | 17 | $if(quotes)$ 18 | 19 | $endif$ 20 | $if(highlighting-css)$ 21 | 24 | $endif$ 25 | $for(css)$ 26 | 27 | $endfor$ 28 | $if(math)$ 29 | $math$ 30 | $endif$ 31 | $for(header-includes)$ 32 | $header-includes$ 33 | $endfor$ 34 | 35 | 36 |
37 | $for(include-before)$ 38 | $include-before$ 39 | $endfor$ 40 | $if(title)$ 41 |
42 |

$title$

43 | $for(author)$ 44 |

$author$

45 | $endfor$ 46 | $if(date)$ 47 |

$date$

48 | $endif$ 49 |
50 | $endif$ 51 | $if(toc)$ 52 | 55 | $endif$ 56 | $body$ 57 | $for(include-after)$ 58 | $include-after$ 59 | $endfor$ 60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /Docs/template.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $for(author-meta)$ 7 | 8 | $endfor$ 9 | $if(date-meta)$ 10 | 11 | $endif$ 12 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 13 | 14 | 15 | 16 | $if(quotes)$ 17 | 18 | $endif$ 19 | $if(highlighting-css)$ 20 | 23 | $endif$ 24 | $for(css)$ 25 | 26 | $endfor$ 27 | $if(math)$ 28 | $math$ 29 | $endif$ 30 | $for(header-includes)$ 31 | $header-includes$ 32 | $endfor$ 33 | 34 | 35 |
36 | $for(include-before)$ 37 | $include-before$ 38 | $endfor$ 39 | $if(title)$ 40 |
41 |

$title$

42 | $for(author)$ 43 |

$author$

44 | $endfor$ 45 | $if(date)$ 46 |

$date$

47 | $endif$ 48 |
49 | $endif$ 50 | $if(toc)$ 51 | 54 | $endif$ 55 | $body$ 56 | $for(include-after)$ 57 | $include-after$ 58 | $endfor$ 59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /Libraries/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mervill/UnityLitJson/10d92bc9f06614eb7ebab26bc37c9acd920cf3cb/Libraries/nunit.framework.dll -------------------------------------------------------------------------------- /LitJson.csproj: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | LitJson 11 | Source 12 | Test 13 | Build 14 | Libraries 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | LitJSON 1.1.1 (UnityLitJson) 2 | ============= 3 | 4 | Merged [HyperHippo's](https://github.com/HyperHippo/litjson) v2 branch onto the main branch. 5 | Added some Unity3D specific type bindings & platform compatibility patches. 6 | 7 | *Note about building* 8 | 9 | I've left the LitJson csproj intact so you can run the tests. When your 10 | ready to use the library in Unity3D I recommend you import the uncompleted 11 | source, there are some #if directives in UnityPlatform.cs that help make the 12 | library work on WinRT (and possibly other platforms in the future). 13 | 14 | LitJSON 1.1.0 15 | ============= 16 | 17 | Implemented type-hinting. 18 | To enable type-hinting set the JsonReader/JsonWriter's `TypeHinting` property to `true`. 19 | Optionally, set the `HintTypeName` and `HintValueName` properties. 20 | These properties are the JSON key names for the type and the data objects. 21 | The defaults are "__type__" and "__value__" respectivly. 22 | 23 | If enabled, the type-hinting system will only include type information for polymorphic fields/properties or collections which contains polymorphic elements. 24 | 25 | ```C# 26 | // As an example, if we had the following schema: 27 | 28 | // Contained in MyAssembly 29 | namespace MyNamespace { 30 | 31 | class Base { 32 | public int x = 1; 33 | } 34 | 35 | class Derived : Base { 36 | public int y = 2; 37 | } 38 | 39 | class Root { 40 | public Base field; 41 | } 42 | 43 | } 44 | 45 | // And we had a root object which contained the following: 46 | var root = new Root(); 47 | root.field = new Derived(); 48 | 49 | // Then serializing the object will include type hinting, since the field type does not match the given type exactly. 50 | JsonWriter writer = new JsonWriter(); 51 | writer.TypeHinting = true; 52 | string json = JsonMapper.ToJson(root, writer); 53 | ``` 54 | 55 | The resultant JSON will be as follows: 56 | 57 | ```JSON 58 | { 59 | "field": { 60 | "__type__": "MyNamespace.Derived, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", 61 | "__value__": { 62 | "x": 1, 63 | "y": 2 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | To property locate the correct type, the [Assembly Qualified Name](http://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname.aspx) is serialized along with the object. 70 | It is the most amount of information that can be given about a particular type. 71 | Although verbose, it is needed to property determine the exact namespace and assembly the type is located in to find it. 72 | In the future, it may be possibly to only specify a namespace and type name as the hint. 73 | This would mean the library would have to search through each assembly for the correct type, which could significantly degrade performance. 74 | 75 | Type-hinting also works for generic collections like `List` and `IDictionary`, and will only include hints for elements that do not match the generic argument exactly. 76 | Type-hinting will also work for non-generic collections, in which case all elements are given serialized with a hint. 77 | 78 | LitJSON 1.0.0 79 | ============= 80 | 81 | JsonIgnore attribute added. 82 | With this attribute, you can specify if an attribute read/written when serializaing or deserializing. 83 | If you specify `[JsonIgnore(JsonIgnoreWhen.Serializing)]` the attribute will be deserialized to, but not serialized. 84 | On the contrary, `[JsonIgnore(JsonIgnoreWhen.Deserializing)]` will serialize the attribute but will ignore it when deserializing. 85 | The default, `[JsonIgnore]` will ignore the attribute completely. 86 | 87 | LitJSON 0.9.0 88 | ============= 89 | 90 | *To Be Announced* 91 | 92 | Additions: 93 | 94 | * Added `JsonData.Keys` property, with type `ICollection`. 95 | 96 | 97 | LitJSON 0.7.0 98 | ============= 99 | 100 | 2013-04-26 101 | 102 | General changes and improvements: 103 | 104 | * Simplified the building mechanism. Dropped the entire autotools 105 | infrastructure, and instead added a single Makefile to compile the project 106 | and run the tests with *GNU make*. 107 | 108 | * Added `SkipNonMembers` property to `JsonReader`. When active, it allows to 109 | parse JSON data using `JsonMapper.ToObject` and ignore any properties 110 | not available in the type `T`. Its default value is `true`. 111 | 112 | * Started moving the documentation into Markdown format. 113 | 114 | * Added a new section to the QuickStart Guide, regarding customisation of 115 | the library's behaviour. 116 | 117 | Bug fixes: 118 | 119 | * Convert `null` properties properly in `JsonData.ToJson`. 120 | 121 | * Read nested arrays in `JsonMapper.ToObject` and `JsonMapper.ToObject` 122 | correctly. 123 | 124 | Contributors for this release: 125 | 126 | - whoo24 127 | - Christopher Dummy 128 | 129 | 130 | LitJSON 0.5.0 131 | ============= 132 | 133 | 2007-10-04 134 | 135 | New features and improvements: 136 | 137 | * The JsonRader class now has two properties to control the reading of data 138 | from an input stream: EndOfInput and EndOfJson. The latter becomes 139 | true whenever a complete piece of JSON text has been read, while the 140 | former is a flag that becomes true when the stream itself reaches the end. 141 | This way, reading multiple JSON texts from the same input stream is 142 | straightforward. 143 | 144 | * Added new base importers in JsonMapper for reading numeric values 145 | correctly into float and double members. 146 | 147 | * Now Enum's can be imported/exported as numeric values. 148 | 149 | * JsonData implements the IEquatable interface now. 150 | 151 | 152 | API changes: 153 | 154 | The following types are new: 155 | enum JsonType 156 | 157 | The following methods are new: 158 | IJsonWrapper.GetJsonType() 159 | IJsonWrapper.SetJsonType() 160 | 161 | The following properties are new: 162 | JsonReader.EndOfInput 163 | 164 | 165 | Bug fixes: 166 | 167 | * Correctly import/export properties that are read-only or write-only. 168 | 169 | * Correctly convert null values when adding them as array elements or 170 | properties to a JsonData instance. 171 | 172 | * Fixed conversion of empty JSON objects and arrays. 173 | 174 | 175 | Thanks to all the contributors that reported problems and suggested fixes 176 | for this release: 177 | Colin Alworth 178 | Ralf Callenberg 179 | andi 180 | 181 | 182 | LitJSON 0.3.0 183 | ============= 184 | 185 | 2007-08-15 186 | 187 | New features and improvements: 188 | 189 | * Exporters and importers. 190 | Custom conversions using the JsonMapper class can be made through 191 | importers and exporters. These are delegates that tell the library how to 192 | perform conversions between non-basic types (i.e. not string, int, long, 193 | double or boolean) and JSON. There are base and custom 194 | exporters/importers. The base exporters and importers are built-in 195 | delegates that currently handle simple conversions between JSON and the 196 | following value types: 197 | 198 | byte 199 | char 200 | DateTime 201 | decimal 202 | sbyte 203 | short 204 | ushort 205 | uint 206 | ulong 207 | 208 | Custom exporters and importers can be defined through 209 | JsonMapper.RegisterExporter and JsonMapper.RegisterImporter, and they 210 | override the built-in conversions. 211 | 212 | * Improved performance of JsonMapper.ToJson() 213 | A static JsonWriter is re-used to reduce the activity in the heap, 214 | improving performance for multiple conversions. 215 | 216 | * Allowing extended grammar 217 | The lexer can now accept single-quoted strings, and comments in the 218 | following forms: 219 | 220 | // Single-line comment 221 | 222 | /* 223 | * Multi-line comment 224 | */ 225 | 226 | This way, certain forms of input coming from JavaScript that don't 227 | necessarily conform to the strict JSON grammar are allowed and succesfully 228 | parsed. 229 | 230 | A JsonReader can be configured to accept only the strict grammar or the 231 | extended one. These extensions are allowed by default. 232 | 233 | * API cleanups and additions. 234 | The following members are new: 235 | 236 | JsonData.Count 237 | JsonMapper.RegisterExporter() 238 | JsonMapper.RegisterImporter() 239 | JsonMapper.UnregisterExporters() 240 | JsonMapper.UnregisterImporters() 241 | JsonReader.AllowComments 242 | JsonReader.AllowSingleQuotedStrings 243 | JsonWriter.Reset() 244 | 245 | The following overloads have been added: 246 | 247 | JsonMapper.ToJson(object obj, JsonWriter writer) 248 | JsonMapper.ToObject(JsonReader reader) 249 | 250 | The following members have been renamed: 251 | 252 | JsonReader.HasReachedEnd is now JsonReader.EndOfJson 253 | 254 | 255 | Bugs fixed: 256 | 257 | * JsonMapper.ToJson() avoids entering an infinite recursion by defining a 258 | max nesting depth. 259 | * The JsonData int indexer now behaves correctly both when it acts as an 260 | array and as an object. 261 | 262 | 263 | LitJSON 0.1.0 264 | ============= 265 | 266 | 2007-08-09 267 | 268 | First release. 269 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LitJSON 2 | ======= 3 | 4 | A *.Net* library to handle conversions from and to JSON (JavaScript Object 5 | Notation) strings. This fork has been edited specificity for use with [Unity3D](http://unity3d.com/). 6 | 7 | Originally created by [lbv](http://lbv.github.io/litjson/). 8 | 9 | ## Installation 10 | 11 | Simply pull the repo and open the .unitypackage to import the library. 12 | 13 | ## Licence 14 | 15 | This is free and unencumbered software released into the public domain. 16 | 17 | For more information, please refer to http://unlicense.org/. 18 | 19 | ## Examples 20 | 21 | For general examples, check out [/Docs/Quickstart/guide.md](https://github.com/VictorySquare/UnityLitJson/blob/master/Docs/quickstart/guide.md) 22 | in this repo, there is also a simple example included in the Unity package. 23 | 24 | ## Compiling 25 | 26 | The LitJson csproj can be used to compile a dll and run the tests. 27 | If you intend to use the library in your Unity3D project I recommend 28 | you import the uncompiled source as there are some #if directives in 29 | Platform.cs that help make the library work on WinRT (and possibly 30 | other platforms in the future). 31 | 32 | ``` 33 | Compiler Directives 34 | 35 | JSON_STANDALONE // Compile without Unity3D dependencies. 36 | 37 | JSON_WINRT // Compile with WinRT compatibility fix. This will 38 | // force compilation in Unity3D even if UNITY_METRO 39 | // is false. It is recommended you only use this if 40 | // you are using JSON_STANDALONE and are building 41 | // for WindowsRT 42 | 43 | ``` 44 | 45 | ## Tests 46 | 47 | This library comes with a set of unit tests using the [NUnit](http://www.nunit.org/) framework. 48 | 49 | ## TODO 50 | 51 | 1. Remove the static writer and just create a new private one each time instead (eliminates need for global lock). 52 | 2. Add more error checks for type hinting. 53 | 3. Add full XMLDocs to public interfaces. 54 | 4. Try to get custom importers/exporters working recursively. 55 | 6. README.md needs more information and examples. 56 | 7. Fix unit tests that cause mono to crash (something with Enums I think). 57 | -------------------------------------------------------------------------------- /Source/Extensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace LitJson.Extensions { 5 | 6 | public static class Extensions { 7 | 8 | public static void WriteProperty(this JsonWriter w,string name,long value){ 9 | w.WritePropertyName(name); 10 | w.Write(value); 11 | } 12 | 13 | public static void WriteProperty(this JsonWriter w,string name,string value){ 14 | w.WritePropertyName(name); 15 | w.Write(value); 16 | } 17 | 18 | public static void WriteProperty(this JsonWriter w,string name,bool value){ 19 | w.WritePropertyName(name); 20 | w.Write(value); 21 | } 22 | 23 | public static void WriteProperty(this JsonWriter w,string name,double value){ 24 | w.WritePropertyName(name); 25 | w.Write(value); 26 | } 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /Source/IJsonWrapper.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System.Collections; 9 | using System.Collections.Specialized; 10 | 11 | namespace LitJson { 12 | 13 | /// 14 | /// This enum contains the possible types a JSON value can have. 15 | /// 16 | public enum JsonType { 17 | None, 18 | Object, 19 | Array, 20 | String, 21 | Natural, 22 | Real, 23 | Boolean 24 | } 25 | 26 | /// 27 | /// Interface that represents a type capable of handling all kinds of JSON data. 28 | /// This is mainly used when mapping objects through JsonMapper, and it's implemented by JsonData. 29 | /// 30 | public interface IJsonWrapper : IList, IOrderedDictionary { 31 | bool IsObject { get; } 32 | bool IsArray { get; } 33 | bool IsString { get; } 34 | bool IsNatural { get; } 35 | bool IsReal { get; } 36 | bool IsBoolean { get; } 37 | 38 | JsonType GetJsonType(); 39 | string GetString(); 40 | long GetNatural(); 41 | double GetReal(); 42 | bool GetBoolean(); 43 | 44 | void SetJsonType(JsonType type); 45 | void SetString(string val); 46 | void SetNatural(long val); 47 | void SetReal(double val); 48 | void SetBoolean(bool val); 49 | 50 | string ToJson(); 51 | void ToJson(JsonWriter writer); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Source/JsonAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LitJson { 5 | 6 | [Flags] 7 | public enum JsonIgnoreWhen { 8 | Never = 0, 9 | Serializing = 1, 10 | Deserializing = 2 11 | } 12 | 13 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 14 | public class JsonIgnore : Attribute { 15 | public JsonIgnoreWhen Usage { get; private set; } 16 | 17 | public JsonIgnore() { 18 | Usage = JsonIgnoreWhen.Serializing | JsonIgnoreWhen.Deserializing; 19 | } 20 | 21 | public JsonIgnore(JsonIgnoreWhen usage) { 22 | Usage = usage; 23 | } 24 | } 25 | 26 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] 27 | public class JsonIgnoreMember : Attribute { 28 | public HashSet Members { get; private set; } 29 | 30 | public JsonIgnoreMember(params string[] members) : this((ICollection)members) { 31 | } 32 | 33 | public JsonIgnoreMember(ICollection members) { 34 | Members = new HashSet(members); 35 | } 36 | } 37 | 38 | /// 39 | /// Attribute to be placed on non-public fields or properties to include them in serialization. 40 | /// 41 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 42 | public class JsonInclude : Attribute { 43 | } 44 | 45 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 46 | public class JsonAlias : Attribute { 47 | public string Alias { get; private set; } 48 | public bool AcceptOriginal { get; private set; } 49 | 50 | public JsonAlias(string aliasName, bool acceptOriginalName = false) { 51 | Alias = aliasName; 52 | AcceptOriginal = acceptOriginalName; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Source/JsonException.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace LitJson { 11 | 12 | /// 13 | /// Base class throwed by LitJSON when a parsing error occurs. 14 | /// 15 | public class JsonException : Exception { 16 | 17 | public JsonException() : base() {} 18 | 19 | internal JsonException(ParserToken token) : base(string.Format("Invalid token '{0}' in input string", token)){ } 20 | 21 | internal JsonException(ParserToken token, Exception inner) : base(string.Format("Invalid token '{0}' in input string", token), inner){ } 22 | 23 | internal JsonException(int c) : base(string.Format("Invalid character '{0}' in input string", (char)c)){ } 24 | 25 | internal JsonException(int c, Exception inner) : base(string.Format("Invalid character '{0}' in input string", (char)c), inner){ } 26 | 27 | public JsonException(string message) : base(message){ } 28 | 29 | public JsonException(string message, Exception inner) : base(message, inner){ } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Source/JsonMockWrapper.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Specialized; 11 | 12 | namespace LitJson { 13 | 14 | /// 15 | /// Mock object implementing IJsonWrapper, to facilitate actions like skipping data more efficiently. 16 | /// 17 | public class JsonMockWrapper : IJsonWrapper { 18 | public bool IsArray { get { return false; } } 19 | public bool IsBoolean { get { return false; } } 20 | public bool IsReal { get { return false; } } 21 | public bool IsNatural { get { return false; } } 22 | public bool IsObject { get { return false; } } 23 | public bool IsString { get { return false; } } 24 | 25 | public bool GetBoolean() { return false; } 26 | public double GetReal() { return 0.0; } 27 | public long GetNatural() { return 0; } 28 | public JsonType GetJsonType() { return JsonType.None; } 29 | public string GetString() { return ""; } 30 | 31 | public void SetBoolean(bool val) {} 32 | public void SetReal(double val) {} 33 | public void SetNatural(long val) {} 34 | public void SetJsonType(JsonType type) {} 35 | public void SetString(string val) {} 36 | 37 | public string ToJson() { return ""; } 38 | public void ToJson(JsonWriter writer) {} 39 | 40 | bool IList.IsFixedSize { get { return true; } } 41 | bool IList.IsReadOnly { get { return true; } } 42 | 43 | object IList.this[int index] { 44 | get { return null; } 45 | set {} 46 | } 47 | 48 | int IList.Add(object value) { return 0; } 49 | void IList.Clear() {} 50 | bool IList.Contains(object value) { return false; } 51 | int IList.IndexOf(object value) { return -1; } 52 | void IList.Insert(int i, object v) {} 53 | void IList.Remove(object value) {} 54 | void IList.RemoveAt(int index) {} 55 | 56 | int ICollection.Count { get { return 0; } } 57 | bool ICollection.IsSynchronized { get { return false; } } 58 | object ICollection.SyncRoot { get { return null; } } 59 | 60 | void ICollection.CopyTo(Array array, int index) {} 61 | 62 | IEnumerator IEnumerable.GetEnumerator() { return null; } 63 | 64 | bool IDictionary.IsFixedSize { get { return true; } } 65 | bool IDictionary.IsReadOnly { get { return true; } } 66 | 67 | ICollection IDictionary.Keys { get { return null; } } 68 | ICollection IDictionary.Values { get { return null; } } 69 | 70 | object IDictionary.this[object key] { 71 | get { return null; } 72 | set {} 73 | } 74 | 75 | void IDictionary.Add(object k, object v) {} 76 | void IDictionary.Clear() {} 77 | bool IDictionary.Contains(object key) { return false; } 78 | void IDictionary.Remove(object key) {} 79 | 80 | IDictionaryEnumerator IDictionary.GetEnumerator() { return null; } 81 | 82 | object IOrderedDictionary.this[int idx] { 83 | get { return null; } 84 | set {} 85 | } 86 | 87 | IDictionaryEnumerator IOrderedDictionary.GetEnumerator() { 88 | return null; 89 | } 90 | 91 | void IOrderedDictionary.Insert(int i, object k, object v) {} 92 | void IOrderedDictionary.RemoveAt(int i) {} 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Source/JsonReader.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using System.Text; 12 | 13 | namespace LitJson { 14 | 15 | public enum JsonToken { 16 | None, 17 | Null, 18 | 19 | ObjectStart, 20 | PropertyName, 21 | ObjectEnd, 22 | 23 | ArrayStart, 24 | ArrayEnd, 25 | 26 | Real, 27 | Natural, 28 | 29 | String, 30 | 31 | Boolean 32 | } 33 | 34 | /// 35 | /// Stream-like access to JSON text. 36 | /// 37 | public class JsonReader { 38 | private static readonly IDictionary> parseTable; 39 | 40 | private Stack automationStack; 41 | private Lexer lexer; 42 | private TextReader reader; 43 | private int currentInput, currentSymbol; 44 | private bool parserInString, parserReturn; 45 | private bool readStarted, readerIsOwned; 46 | 47 | public bool AllowComments { 48 | get { return lexer.AllowComments; } 49 | set { lexer.AllowComments = value; } 50 | } 51 | 52 | public bool AllowSingleQuotedStrings { 53 | get { return lexer.AllowSingleQuotedStrings; } 54 | set { lexer.AllowSingleQuotedStrings = value; } 55 | } 56 | 57 | public bool SkipNonMembers { get; set; } 58 | public bool TypeHinting { get; set; } 59 | public string HintTypeName { get; set; } 60 | public string HintValueName { get; set; } 61 | 62 | public bool EndOfInput { get; private set; } 63 | public bool EndOfJson { get; private set; } 64 | public JsonToken Token { get; private set; } 65 | public object Value { get; private set; } 66 | 67 | public JsonReader(string json) : this(new StringReader(json), true) { 68 | } 69 | 70 | public JsonReader(TextReader reader) : this(reader, false) { 71 | } 72 | 73 | private JsonReader(TextReader reader, bool owned) { 74 | if (reader == null) { 75 | throw new ArgumentNullException("reader"); 76 | } 77 | parserInString = false; 78 | parserReturn = false; 79 | 80 | readStarted = false; 81 | automationStack = new Stack(); 82 | automationStack.Push((int)ParserToken.End); 83 | automationStack.Push((int)ParserToken.Text); 84 | 85 | lexer = new Lexer(reader); 86 | 87 | EndOfInput = false; 88 | EndOfJson = false; 89 | 90 | SkipNonMembers = true; 91 | 92 | this.reader = reader; 93 | readerIsOwned = owned; 94 | 95 | TypeHinting = false; 96 | HintTypeName = "__type__"; 97 | HintValueName = "__value__"; 98 | } 99 | 100 | static JsonReader() { 101 | // Populate parse table 102 | // See section A.2. of the manual for details 103 | parseTable = new Dictionary>(); 104 | 105 | TableAddRow(ParserToken.Array); 106 | TableAddCol(ParserToken.Array, '[', 107 | '[', 108 | (int)ParserToken.ArrayPrime); 109 | 110 | TableAddRow(ParserToken.ArrayPrime); 111 | TableAddCol(ParserToken.ArrayPrime, '"', 112 | (int)ParserToken.Value, 113 | (int)ParserToken.ValueRest, 114 | ']'); 115 | 116 | TableAddCol(ParserToken.ArrayPrime, '[', 117 | (int)ParserToken.Value, 118 | (int)ParserToken.ValueRest, 119 | ']'); 120 | 121 | TableAddCol(ParserToken.ArrayPrime, ']', 122 | ']'); 123 | 124 | TableAddCol(ParserToken.ArrayPrime, '{', 125 | (int)ParserToken.Value, 126 | (int)ParserToken.ValueRest, 127 | ']'); 128 | 129 | TableAddCol(ParserToken.ArrayPrime, (int)ParserToken.Number, 130 | (int)ParserToken.Value, 131 | (int)ParserToken.ValueRest, 132 | ']'); 133 | 134 | TableAddCol(ParserToken.ArrayPrime, (int)ParserToken.True, 135 | (int)ParserToken.Value, 136 | (int)ParserToken.ValueRest, 137 | ']'); 138 | 139 | TableAddCol(ParserToken.ArrayPrime, (int)ParserToken.False, 140 | (int)ParserToken.Value, 141 | (int)ParserToken.ValueRest, 142 | ']'); 143 | 144 | TableAddCol(ParserToken.ArrayPrime, (int)ParserToken.Null, 145 | (int)ParserToken.Value, 146 | (int)ParserToken.ValueRest, 147 | ']'); 148 | 149 | TableAddRow(ParserToken.Object); 150 | TableAddCol(ParserToken.Object, '{', 151 | '{', 152 | (int)ParserToken.ObjectPrime); 153 | 154 | TableAddRow(ParserToken.ObjectPrime); 155 | TableAddCol(ParserToken.ObjectPrime, '"', 156 | (int)ParserToken.Pair, 157 | (int)ParserToken.PairRest, 158 | '}'); 159 | 160 | TableAddCol(ParserToken.ObjectPrime, '}', 161 | '}'); 162 | 163 | TableAddRow(ParserToken.Pair); 164 | TableAddCol(ParserToken.Pair, '"', 165 | (int)ParserToken.String, 166 | ':', 167 | (int)ParserToken.Value); 168 | 169 | TableAddRow(ParserToken.PairRest); 170 | TableAddCol(ParserToken.PairRest, ',', 171 | ',', 172 | (int)ParserToken.Pair, 173 | (int)ParserToken.PairRest); 174 | 175 | TableAddCol(ParserToken.PairRest, '}', 176 | (int)ParserToken.Epsilon); 177 | 178 | TableAddRow(ParserToken.String); 179 | TableAddCol(ParserToken.String, '"', 180 | '"', 181 | (int)ParserToken.CharSeq, 182 | '"'); 183 | 184 | TableAddRow(ParserToken.Text); 185 | TableAddCol(ParserToken.Text, '[', 186 | (int)ParserToken.Array); 187 | TableAddCol(ParserToken.Text, '{', 188 | (int)ParserToken.Object); 189 | 190 | TableAddRow(ParserToken.Value); 191 | TableAddCol(ParserToken.Value, '"', 192 | (int)ParserToken.String); 193 | TableAddCol(ParserToken.Value, '[', 194 | (int)ParserToken.Array); 195 | TableAddCol(ParserToken.Value, '{', 196 | (int)ParserToken.Object); 197 | TableAddCol(ParserToken.Value, (int)ParserToken.Number, 198 | (int)ParserToken.Number); 199 | TableAddCol(ParserToken.Value, (int)ParserToken.True, 200 | (int)ParserToken.True); 201 | TableAddCol(ParserToken.Value, (int)ParserToken.False, 202 | (int)ParserToken.False); 203 | TableAddCol(ParserToken.Value, (int)ParserToken.Null, 204 | (int)ParserToken.Null); 205 | 206 | TableAddRow(ParserToken.ValueRest); 207 | TableAddCol(ParserToken.ValueRest, ',', 208 | ',', 209 | (int)ParserToken.Value, 210 | (int)ParserToken.ValueRest); 211 | 212 | TableAddCol(ParserToken.ValueRest, ']', 213 | (int)ParserToken.Epsilon); 214 | } 215 | 216 | private static void TableAddCol(ParserToken row, int col, params int[] symbols) { 217 | parseTable[(int)row].Add(col, symbols); 218 | } 219 | 220 | private static void TableAddRow(ParserToken rule) { 221 | parseTable.Add((int)rule, new Dictionary()); 222 | } 223 | 224 | private void ProcessNumber(string number) { 225 | if (number.IndexOf('.') != -1 || 226 | number.IndexOf('e') != -1 || 227 | number.IndexOf('E') != -1) { 228 | 229 | double real; 230 | if (double.TryParse (number, out real)) { 231 | Token = JsonToken.Real; 232 | Value = real; 233 | return; 234 | } 235 | } 236 | long natural; 237 | if (long.TryParse (number, out natural)) { 238 | Token = JsonToken.Natural; 239 | Value = natural; 240 | return; 241 | } 242 | ulong unsignednatural; 243 | if (ulong.TryParse(number, out unsignednatural)) { 244 | Token = JsonToken.Natural; 245 | Value = unsignednatural; 246 | return; 247 | } 248 | decimal decimalreal; 249 | if (Decimal.TryParse(number, out decimalreal)) { 250 | Token = JsonToken.Real; 251 | Value = decimalreal; 252 | 253 | return; 254 | } 255 | // Shouldn't happen, but just in case, return something 256 | Token = JsonToken.Natural; 257 | Value = 0; 258 | 259 | throw new JsonException(string.Format("Failed to parse number '{0}'", number)); 260 | } 261 | 262 | private void ProcessSymbol() { 263 | if (currentSymbol == '[') { 264 | Token = JsonToken.ArrayStart; 265 | parserReturn = true; 266 | } else if (currentSymbol == ']') { 267 | Token = JsonToken.ArrayEnd; 268 | parserReturn = true; 269 | } else if (currentSymbol == '{') { 270 | Token = JsonToken.ObjectStart; 271 | parserReturn = true; 272 | } else if (currentSymbol == '}') { 273 | Token = JsonToken.ObjectEnd; 274 | parserReturn = true; 275 | } else if (currentSymbol == '"') { 276 | if (parserInString) { 277 | parserInString = false; 278 | parserReturn = true; 279 | } else { 280 | if (Token == JsonToken.None) { 281 | Token = JsonToken.String; 282 | } 283 | parserInString = true; 284 | } 285 | } else if (currentSymbol == (int)ParserToken.CharSeq) { 286 | Value = lexer.StringValue; 287 | } else if (currentSymbol == (int)ParserToken.False) { 288 | Token = JsonToken.Boolean; 289 | Value = false; 290 | parserReturn = true; 291 | } else if (currentSymbol == (int)ParserToken.Null) { 292 | Token = JsonToken.Null; 293 | parserReturn = true; 294 | } else if (currentSymbol == (int)ParserToken.Number) { 295 | ProcessNumber (lexer.StringValue); 296 | parserReturn = true; 297 | } else if (currentSymbol == (int)ParserToken.Pair) { 298 | Token = JsonToken.PropertyName; 299 | } else if (currentSymbol == (int)ParserToken.True) { 300 | Token = JsonToken.Boolean; 301 | Value = true; 302 | parserReturn = true; 303 | } 304 | } 305 | 306 | private bool ReadToken() { 307 | if (EndOfInput) { 308 | return false; 309 | } 310 | lexer.NextToken(); 311 | if (lexer.EndOfInput) { 312 | Close(); 313 | return false; 314 | } 315 | currentInput = lexer.Token; 316 | return true; 317 | } 318 | 319 | public void Close() { 320 | if (EndOfInput) { 321 | return; 322 | } 323 | EndOfInput = true; 324 | EndOfJson = true; 325 | if (readerIsOwned) { 326 | reader.Close(); 327 | } 328 | reader = null; 329 | } 330 | 331 | public bool Read() { 332 | if (EndOfInput) { 333 | return false; 334 | } 335 | if (EndOfJson) { 336 | EndOfJson = false; 337 | automationStack.Clear(); 338 | automationStack.Push((int)ParserToken.End); 339 | automationStack.Push((int)ParserToken.Text); 340 | } 341 | parserInString = false; 342 | parserReturn = false; 343 | 344 | Token = JsonToken.None; 345 | Value = null; 346 | 347 | if (!readStarted) { 348 | readStarted = true; 349 | if (!ReadToken()) { 350 | return false; 351 | } 352 | } 353 | int[] entrySymbols; 354 | while (true) { 355 | if (parserReturn) { 356 | if (automationStack.Peek() == (int)ParserToken.End) { 357 | EndOfJson = true; 358 | } 359 | return true; 360 | } 361 | currentSymbol = automationStack.Pop(); 362 | ProcessSymbol(); 363 | if (currentSymbol == currentInput) { 364 | if (!ReadToken()) { 365 | if (automationStack.Peek() != (int)ParserToken.End) { 366 | throw new JsonException("Input doesn't evaluate to proper JSON text"); 367 | } 368 | if (parserReturn) { 369 | return true; 370 | } 371 | return false; 372 | } 373 | continue; 374 | } 375 | try { 376 | entrySymbols = parseTable[currentSymbol][currentInput]; 377 | } catch (KeyNotFoundException e) { 378 | throw new JsonException((ParserToken)currentInput, e); 379 | } 380 | if (entrySymbols[0] == (int)ParserToken.Epsilon) { 381 | continue; 382 | } 383 | for (int i = entrySymbols.Length - 1; i >= 0; i--) { 384 | automationStack.Push(entrySymbols[i]); 385 | } 386 | } 387 | } 388 | } 389 | 390 | } 391 | -------------------------------------------------------------------------------- /Source/JsonWriter.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Globalization; 11 | using System.IO; 12 | using System.Text; 13 | 14 | namespace LitJson { 15 | 16 | internal enum Condition { 17 | InArray, 18 | InObject, 19 | NotAProperty, 20 | Property, 21 | Value 22 | } 23 | 24 | internal class WriterContext { 25 | public int Count, Padding; 26 | public bool InArray, InObject; 27 | public bool ExpectingValue; 28 | } 29 | 30 | /// 31 | /// Stream-like facility to output JSON text. 32 | /// 33 | public class JsonWriter { 34 | private static readonly NumberFormatInfo numberFormat; 35 | 36 | private WriterContext context; 37 | private Stack ctxStack; 38 | private bool hasReachedEnd; 39 | private char[] hexSeq; 40 | private int indentation, indentValue; 41 | private StringBuilder stringBuilder; 42 | 43 | public int IndentValue { 44 | get { return indentValue; } 45 | set { 46 | indentation = (indentation / indentValue) * value; 47 | indentValue = value; 48 | } 49 | } 50 | 51 | public bool PrettyPrint { get; set; } 52 | public bool Validate { get; set; } 53 | public bool TypeHinting { get; set; } 54 | public string HintTypeName { get; set; } 55 | public string HintValueName { get; set; } 56 | 57 | public TextWriter TextWriter { get; private set; } 58 | 59 | static JsonWriter() { 60 | numberFormat = NumberFormatInfo.InvariantInfo; 61 | } 62 | 63 | public JsonWriter() { 64 | stringBuilder = new StringBuilder(); 65 | TextWriter = new StringWriter(stringBuilder); 66 | Init(); 67 | } 68 | 69 | public JsonWriter(StringBuilder sb) : this(new StringWriter(sb)) { 70 | } 71 | 72 | public JsonWriter(TextWriter writer) { 73 | if (writer == null) { 74 | throw new ArgumentNullException("writer"); 75 | } 76 | this.TextWriter = writer; 77 | Init(); 78 | } 79 | 80 | private void DoValidation(Condition cond) { 81 | if (!context.ExpectingValue) { 82 | context.Count++; 83 | } 84 | if (!Validate) { 85 | return; 86 | } 87 | if (hasReachedEnd) { 88 | throw new JsonException("A complete JSON symbol has already been written"); 89 | } 90 | switch (cond) { 91 | case Condition.InArray: 92 | if (!context.InArray) { 93 | throw new JsonException("Can't close an array here"); 94 | } 95 | break; 96 | case Condition.InObject: 97 | if (!context.InObject || context.ExpectingValue) { 98 | throw new JsonException("Can't close an object here"); 99 | } 100 | break; 101 | case Condition.NotAProperty: 102 | if (context.InObject && !context.ExpectingValue) { 103 | throw new JsonException("Expected a property in obj? "+context.InObject+" expect val? "+context.ExpectingValue+" <"+stringBuilder.ToString()+">"); 104 | } 105 | break; 106 | 107 | case Condition.Property: 108 | if (!context.InObject || context.ExpectingValue) { 109 | throw new JsonException("Can't add a property here"); 110 | } 111 | break; 112 | case Condition.Value: 113 | if (!context.InArray && 114 | (!context.InObject || !context.ExpectingValue)) { 115 | throw new JsonException("Can't add a value here"); 116 | } 117 | break; 118 | } 119 | } 120 | 121 | private void Init() { 122 | hasReachedEnd = false; 123 | hexSeq = new char[4]; 124 | indentation = 0; 125 | indentValue = 4; 126 | PrettyPrint = false; 127 | Validate = true; 128 | 129 | TypeHinting = false; 130 | HintTypeName = "__type__"; 131 | HintValueName = "__value__"; 132 | 133 | ctxStack = new Stack(); 134 | context = new WriterContext(); 135 | ctxStack.Push(context); 136 | } 137 | 138 | private static void IntToHex(int n, char[] hex) { 139 | int num; 140 | for (int i = 0; i < 4; i++) { 141 | num = n % 16; 142 | if (num < 10) { 143 | hex[3 - i] = (char) ('0' + num); 144 | } else { 145 | hex[3 - i] = (char) ('A' + (num - 10)); 146 | } 147 | n >>= 4; 148 | } 149 | } 150 | 151 | private void Indent() { 152 | if (PrettyPrint) { 153 | indentation += indentValue; 154 | } 155 | } 156 | 157 | private void Put(string str) { 158 | if (PrettyPrint && !context.ExpectingValue) { 159 | for (int i = 0; i < indentation; i++) { 160 | TextWriter.Write(' '); 161 | } 162 | } 163 | TextWriter.Write(str); 164 | } 165 | 166 | private void PutNewline(bool addComma = true) { 167 | if (addComma && !context.ExpectingValue && context.Count > 1) { 168 | TextWriter.Write(','); 169 | } 170 | if (PrettyPrint && !context.ExpectingValue) { 171 | TextWriter.Write(Environment.NewLine); 172 | } 173 | } 174 | 175 | private void PutString(string str) { 176 | Put(string.Empty); 177 | TextWriter.Write('"'); 178 | int n = str.Length; 179 | for (int i = 0; i < n; i++) { 180 | switch (str[i]) { 181 | case '\n': 182 | TextWriter.Write("\\n"); 183 | continue; 184 | case '\r': 185 | TextWriter.Write("\\r"); 186 | continue; 187 | case '\t': 188 | TextWriter.Write("\\t"); 189 | continue; 190 | case '"': 191 | case '\\': 192 | TextWriter.Write('\\'); 193 | TextWriter.Write(str[i]); 194 | continue; 195 | case '\f': 196 | TextWriter.Write("\\f"); 197 | continue; 198 | case '\b': 199 | TextWriter.Write("\\b"); 200 | continue; 201 | } 202 | if ((int)str[i] >= 32 && (int)str[i] <= 126) { 203 | TextWriter.Write(str[i]); 204 | continue; 205 | } 206 | // Default, turn into a \uXXXX sequence 207 | IntToHex ((int)str[i], hexSeq); 208 | TextWriter.Write("\\u"); 209 | TextWriter.Write(hexSeq); 210 | } 211 | TextWriter.Write('"'); 212 | } 213 | 214 | private void Unindent() { 215 | if (PrettyPrint) { 216 | indentation -= indentValue; 217 | } 218 | } 219 | 220 | public override string ToString() { 221 | if (stringBuilder == null) { 222 | return string.Empty; 223 | } 224 | return stringBuilder.ToString(); 225 | } 226 | 227 | public void Reset() { 228 | hasReachedEnd = false; 229 | 230 | ctxStack.Clear(); 231 | context = new WriterContext(); 232 | ctxStack.Push(context); 233 | 234 | if (stringBuilder != null) { 235 | stringBuilder.Remove(0, stringBuilder.Length); 236 | } 237 | } 238 | 239 | public void Write(bool boolean) { 240 | DoValidation(Condition.Value); 241 | PutNewline(); 242 | Put(boolean ? "true" : "false"); 243 | context.ExpectingValue = false; 244 | } 245 | 246 | public void Write(double number) { 247 | DoValidation(Condition.Value); 248 | PutNewline(); 249 | string str = number.ToString("R", numberFormat); 250 | Put(str); 251 | if (str.IndexOf('.') == -1 && str.IndexOf('E') == -1) { 252 | TextWriter.Write(".0"); 253 | } 254 | context.ExpectingValue = false; 255 | } 256 | 257 | public void Write(decimal number) { 258 | DoValidation(Condition.Value); 259 | PutNewline(); 260 | Put(Convert.ToString(number, numberFormat)); 261 | context.ExpectingValue = false; 262 | } 263 | 264 | public void Write(long number) { 265 | DoValidation(Condition.Value); 266 | PutNewline(); 267 | Put(Convert.ToString(number, numberFormat)); 268 | context.ExpectingValue = false; 269 | } 270 | 271 | public void Write(ulong number) { 272 | DoValidation(Condition.Value); 273 | PutNewline(); 274 | Put(Convert.ToString(number, numberFormat)); 275 | context.ExpectingValue = false; 276 | } 277 | 278 | public void Write(string str) { 279 | DoValidation(Condition.Value); 280 | PutNewline(); 281 | if (str == null) { 282 | Put("null"); 283 | } else { 284 | PutString(str); 285 | } 286 | context.ExpectingValue = false; 287 | } 288 | 289 | public void WriteArrayEnd() { 290 | DoValidation(Condition.InArray); 291 | PutNewline(false); 292 | ctxStack.Pop(); 293 | if (ctxStack.Count == 1) { 294 | hasReachedEnd = true; 295 | } else { 296 | context = ctxStack.Peek(); 297 | context.ExpectingValue = false; 298 | } 299 | Unindent(); 300 | Put("]"); 301 | } 302 | 303 | public void WriteArrayStart() { 304 | DoValidation(Condition.NotAProperty); 305 | PutNewline(); 306 | Put("["); 307 | context = new WriterContext (); 308 | context.InArray = true; 309 | ctxStack.Push (context); 310 | Indent(); 311 | } 312 | 313 | public void WriteObjectEnd() { 314 | DoValidation(Condition.InObject); 315 | PutNewline(false); 316 | ctxStack.Pop(); 317 | if (ctxStack.Count == 1) { 318 | hasReachedEnd = true; 319 | } else { 320 | context = ctxStack.Peek(); 321 | context.ExpectingValue = false; 322 | } 323 | Unindent(); 324 | Put("}"); 325 | } 326 | 327 | public void WriteObjectStart() { 328 | DoValidation(Condition.NotAProperty); 329 | PutNewline(); 330 | Put("{"); 331 | context = new WriterContext(); 332 | context.InObject = true; 333 | ctxStack.Push(context); 334 | Indent(); 335 | } 336 | 337 | public void WritePropertyName(string name) { 338 | DoValidation(Condition.Property); 339 | PutNewline(); 340 | PutString(name); 341 | if (PrettyPrint) { 342 | if (name.Length > context.Padding) { 343 | context.Padding = name.Length; 344 | } 345 | for (int i = context.Padding - name.Length; i >= 0; i--) { 346 | TextWriter.Write(' '); 347 | } 348 | TextWriter.Write(": "); 349 | } else { 350 | TextWriter.Write(':'); 351 | } 352 | context.ExpectingValue = true; 353 | } 354 | } 355 | 356 | } 357 | -------------------------------------------------------------------------------- /Source/Lexer.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using System.Text; 12 | 13 | namespace LitJson { 14 | 15 | internal class FsmContext { 16 | public Lexer L; 17 | public bool Return; 18 | public int NextState, StateStack; 19 | } 20 | 21 | /// 22 | /// JSON lexer implementation based on a finite state machine. 23 | /// 24 | internal class Lexer { 25 | private delegate bool StateHandler (FsmContext ctx); 26 | 27 | private static int[] returnTable; 28 | private static StateHandler[] handlerTable; 29 | 30 | private int inputBuffer, inputChar, state, unichar; 31 | private FsmContext context; 32 | private TextReader reader; 33 | private StringBuilder stringBuffer; 34 | 35 | public bool AllowComments { get; set; } 36 | public bool AllowSingleQuotedStrings { get; set; } 37 | 38 | public bool EndOfInput { get; private set; } 39 | public int Token { get; private set; } 40 | public string StringValue { get; private set; } 41 | 42 | static Lexer() { 43 | PopulateFsmTables(); 44 | } 45 | 46 | public Lexer(TextReader reader) { 47 | AllowComments = true; 48 | AllowSingleQuotedStrings = true; 49 | 50 | inputBuffer = 0; 51 | stringBuffer = new StringBuilder(128); 52 | state = 1; 53 | EndOfInput = false; 54 | this.reader = reader; 55 | 56 | context = new FsmContext(); 57 | context.L = this; 58 | } 59 | 60 | private static int HexValue(int digit) { 61 | switch (digit) { 62 | case 'a': 63 | case 'A': 64 | return 10; 65 | case 'b': 66 | case 'B': 67 | return 11; 68 | case 'c': 69 | case 'C': 70 | return 12; 71 | case 'd': 72 | case 'D': 73 | return 13; 74 | case 'e': 75 | case 'E': 76 | return 14; 77 | case 'f': 78 | case 'F': 79 | return 15; 80 | default: 81 | return digit - '0'; 82 | } 83 | } 84 | 85 | private static void PopulateFsmTables() { 86 | // See section A.1. of the manual for details of the finite state machine. 87 | handlerTable = new StateHandler[28] { 88 | State1, 89 | State2, 90 | State3, 91 | State4, 92 | State5, 93 | State6, 94 | State7, 95 | State8, 96 | State9, 97 | State10, 98 | State11, 99 | State12, 100 | State13, 101 | State14, 102 | State15, 103 | State16, 104 | State17, 105 | State18, 106 | State19, 107 | State20, 108 | State21, 109 | State22, 110 | State23, 111 | State24, 112 | State25, 113 | State26, 114 | State27, 115 | State28 116 | }; 117 | 118 | returnTable = new int[28] { 119 | (int)ParserToken.Char, 120 | 0, 121 | (int)ParserToken.Number, 122 | (int)ParserToken.Number, 123 | 0, 124 | (int)ParserToken.Number, 125 | 0, 126 | (int)ParserToken.Number, 127 | 0, 128 | 0, 129 | (int)ParserToken.True, 130 | 0, 131 | 0, 132 | 0, 133 | (int)ParserToken.False, 134 | 0, 135 | 0, 136 | (int)ParserToken.Null, 137 | (int)ParserToken.CharSeq, 138 | (int)ParserToken.Char, 139 | 0, 140 | 0, 141 | (int)ParserToken.CharSeq, 142 | (int)ParserToken.Char, 143 | 0, 144 | 0, 145 | 0, 146 | 0 147 | }; 148 | } 149 | 150 | private static char ProcessEscChar (int escChar) { 151 | switch (escChar) { 152 | case '"': 153 | case '\'': 154 | case '\\': 155 | case '/': 156 | return Convert.ToChar(escChar); 157 | case 'n': 158 | return '\n'; 159 | case 't': 160 | return '\t'; 161 | case 'r': 162 | return '\r'; 163 | case 'b': 164 | return '\b'; 165 | case 'f': 166 | return '\f'; 167 | default: 168 | // Unreachable 169 | return '?'; 170 | } 171 | } 172 | 173 | private static bool State1(FsmContext ctx) { 174 | while (ctx.L.GetChar()) { 175 | if (ctx.L.inputChar == ' ' || 176 | ctx.L.inputChar >= '\t' && ctx.L.inputChar <= '\r') { 177 | continue; 178 | } 179 | if (ctx.L.inputChar >= '1' && ctx.L.inputChar <= '9') { 180 | ctx.L.stringBuffer.Append ((char) ctx.L.inputChar); 181 | ctx.NextState = 3; 182 | return true; 183 | } 184 | switch (ctx.L.inputChar) { 185 | case '"': 186 | ctx.NextState = 19; 187 | ctx.Return = true; 188 | return true; 189 | case ',': 190 | case ':': 191 | case '[': 192 | case ']': 193 | case '{': 194 | case '}': 195 | ctx.NextState = 1; 196 | ctx.Return = true; 197 | return true; 198 | case '-': 199 | ctx.L.stringBuffer.Append ((char) ctx.L.inputChar); 200 | ctx.NextState = 2; 201 | return true; 202 | case '0': 203 | ctx.L.stringBuffer.Append ((char) ctx.L.inputChar); 204 | ctx.NextState = 4; 205 | return true; 206 | case 'f': 207 | ctx.NextState = 12; 208 | return true; 209 | case 'n': 210 | ctx.NextState = 16; 211 | return true; 212 | case 't': 213 | ctx.NextState = 9; 214 | return true; 215 | case '\'': 216 | if (!ctx.L.AllowSingleQuotedStrings) { 217 | return false; 218 | } 219 | ctx.L.inputChar = '"'; 220 | ctx.NextState = 23; 221 | ctx.Return = true; 222 | return true; 223 | case '/': 224 | if (!ctx.L.AllowComments) { 225 | return false; 226 | } 227 | ctx.NextState = 25; 228 | return true; 229 | default: 230 | return false; 231 | } 232 | } 233 | return true; 234 | } 235 | 236 | private static bool State2(FsmContext ctx) { 237 | ctx.L.GetChar(); 238 | if (ctx.L.inputChar >= '1' && ctx.L.inputChar <= '9') { 239 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 240 | ctx.NextState = 3; 241 | return true; 242 | } 243 | switch (ctx.L.inputChar) { 244 | case '0': 245 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 246 | ctx.NextState = 4; 247 | return true; 248 | default: 249 | return false; 250 | } 251 | } 252 | 253 | private static bool State3(FsmContext ctx) { 254 | while (ctx.L.GetChar()) { 255 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9') { 256 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 257 | continue; 258 | } 259 | if (ctx.L.inputChar == ' ' || 260 | ctx.L.inputChar >= '\t' && ctx.L.inputChar <= '\r') { 261 | ctx.Return = true; 262 | ctx.NextState = 1; 263 | return true; 264 | } 265 | switch (ctx.L.inputChar) { 266 | case ',': 267 | case ']': 268 | case '}': 269 | ctx.L.UngetChar(); 270 | ctx.Return = true; 271 | ctx.NextState = 1; 272 | return true; 273 | case '.': 274 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 275 | ctx.NextState = 5; 276 | return true; 277 | case 'e': 278 | case 'E': 279 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 280 | ctx.NextState = 7; 281 | return true; 282 | default: 283 | return false; 284 | } 285 | } 286 | return true; 287 | } 288 | 289 | private static bool State4(FsmContext ctx) { 290 | ctx.L.GetChar (); 291 | if (ctx.L.inputChar == ' ' || 292 | ctx.L.inputChar >= '\t' && ctx.L.inputChar <= '\r') { 293 | ctx.Return = true; 294 | ctx.NextState = 1; 295 | return true; 296 | } 297 | switch (ctx.L.inputChar) { 298 | case ',': 299 | case ']': 300 | case '}': 301 | ctx.L.UngetChar (); 302 | ctx.Return = true; 303 | ctx.NextState = 1; 304 | return true; 305 | case '.': 306 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 307 | ctx.NextState = 5; 308 | return true; 309 | case 'e': 310 | case 'E': 311 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 312 | ctx.NextState = 7; 313 | return true; 314 | default: 315 | return false; 316 | } 317 | } 318 | 319 | private static bool State5(FsmContext ctx) { 320 | ctx.L.GetChar(); 321 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9') { 322 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 323 | ctx.NextState = 6; 324 | return true; 325 | } 326 | return false; 327 | } 328 | 329 | private static bool State6(FsmContext ctx) { 330 | while (ctx.L.GetChar()) { 331 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9') { 332 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 333 | continue; 334 | } 335 | if (ctx.L.inputChar == ' ' || 336 | ctx.L.inputChar >= '\t' && ctx.L.inputChar <= '\r') { 337 | ctx.Return = true; 338 | ctx.NextState = 1; 339 | return true; 340 | } 341 | switch (ctx.L.inputChar) { 342 | case ',': 343 | case ']': 344 | case '}': 345 | ctx.L.UngetChar(); 346 | ctx.Return = true; 347 | ctx.NextState = 1; 348 | return true; 349 | case 'e': 350 | case 'E': 351 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 352 | ctx.NextState = 7; 353 | return true; 354 | default: 355 | return false; 356 | } 357 | } 358 | return true; 359 | } 360 | 361 | private static bool State7(FsmContext ctx) { 362 | ctx.L.GetChar(); 363 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9') { 364 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 365 | ctx.NextState = 8; 366 | return true; 367 | } 368 | switch (ctx.L.inputChar) { 369 | case '+': 370 | case '-': 371 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 372 | ctx.NextState = 8; 373 | return true; 374 | default: 375 | return false; 376 | } 377 | } 378 | 379 | private static bool State8(FsmContext ctx) { 380 | while (ctx.L.GetChar()) { 381 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9') { 382 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 383 | continue; 384 | } 385 | if (ctx.L.inputChar == ' ' || 386 | ctx.L.inputChar >= '\t' && ctx.L.inputChar <= '\r') { 387 | ctx.Return = true; 388 | ctx.NextState = 1; 389 | return true; 390 | } 391 | switch (ctx.L.inputChar) { 392 | case ',': 393 | case ']': 394 | case '}': 395 | ctx.L.UngetChar(); 396 | ctx.Return = true; 397 | ctx.NextState = 1; 398 | return true; 399 | default: 400 | return false; 401 | } 402 | } 403 | return true; 404 | } 405 | 406 | private static bool State9(FsmContext ctx) { 407 | ctx.L.GetChar(); 408 | switch (ctx.L.inputChar) { 409 | case 'r': 410 | ctx.NextState = 10; 411 | return true; 412 | default: 413 | return false; 414 | } 415 | } 416 | 417 | private static bool State10(FsmContext ctx) { 418 | ctx.L.GetChar(); 419 | switch (ctx.L.inputChar) { 420 | case 'u': 421 | ctx.NextState = 11; 422 | return true; 423 | default: 424 | return false; 425 | } 426 | } 427 | 428 | private static bool State11(FsmContext ctx) { 429 | ctx.L.GetChar(); 430 | switch (ctx.L.inputChar) { 431 | case 'e': 432 | ctx.Return = true; 433 | ctx.NextState = 1; 434 | return true; 435 | default: 436 | return false; 437 | } 438 | } 439 | 440 | private static bool State12(FsmContext ctx) { 441 | ctx.L.GetChar(); 442 | switch (ctx.L.inputChar) { 443 | case 'a': 444 | ctx.NextState = 13; 445 | return true; 446 | default: 447 | return false; 448 | } 449 | } 450 | 451 | private static bool State13(FsmContext ctx) { 452 | ctx.L.GetChar(); 453 | switch (ctx.L.inputChar) { 454 | case 'l': 455 | ctx.NextState = 14; 456 | return true; 457 | default: 458 | return false; 459 | } 460 | } 461 | 462 | private static bool State14(FsmContext ctx) { 463 | ctx.L.GetChar(); 464 | switch (ctx.L.inputChar) { 465 | case 's': 466 | ctx.NextState = 15; 467 | return true; 468 | default: 469 | return false; 470 | } 471 | } 472 | 473 | private static bool State15(FsmContext ctx) { 474 | ctx.L.GetChar(); 475 | switch (ctx.L.inputChar) { 476 | case 'e': 477 | ctx.Return = true; 478 | ctx.NextState = 1; 479 | return true; 480 | default: 481 | return false; 482 | } 483 | } 484 | 485 | private static bool State16(FsmContext ctx) { 486 | ctx.L.GetChar(); 487 | switch (ctx.L.inputChar) { 488 | case 'u': 489 | ctx.NextState = 17; 490 | return true; 491 | default: 492 | return false; 493 | } 494 | } 495 | 496 | private static bool State17(FsmContext ctx) { 497 | ctx.L.GetChar(); 498 | switch (ctx.L.inputChar) { 499 | case 'l': 500 | ctx.NextState = 18; 501 | return true; 502 | default: 503 | return false; 504 | } 505 | } 506 | 507 | private static bool State18(FsmContext ctx) { 508 | ctx.L.GetChar(); 509 | switch (ctx.L.inputChar) { 510 | case 'l': 511 | ctx.Return = true; 512 | ctx.NextState = 1; 513 | return true; 514 | default: 515 | return false; 516 | } 517 | } 518 | 519 | private static bool State19(FsmContext ctx) { 520 | while (ctx.L.GetChar()) { 521 | switch (ctx.L.inputChar) { 522 | case '"': 523 | ctx.L.UngetChar (); 524 | ctx.Return = true; 525 | ctx.NextState = 20; 526 | return true; 527 | case '\\': 528 | ctx.StateStack = 19; 529 | ctx.NextState = 21; 530 | return true; 531 | default: 532 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 533 | continue; 534 | } 535 | } 536 | return true; 537 | } 538 | 539 | private static bool State20(FsmContext ctx) { 540 | ctx.L.GetChar(); 541 | switch (ctx.L.inputChar) { 542 | case '"': 543 | ctx.Return = true; 544 | ctx.NextState = 1; 545 | return true; 546 | default: 547 | return false; 548 | } 549 | } 550 | 551 | private static bool State21(FsmContext ctx) { 552 | ctx.L.GetChar(); 553 | switch (ctx.L.inputChar) { 554 | case 'u': 555 | ctx.NextState = 22; 556 | return true; 557 | case '"': 558 | case '\'': 559 | case '/': 560 | case '\\': 561 | case 'b': 562 | case 'f': 563 | case 'n': 564 | case 'r': 565 | case 't': 566 | ctx.L.stringBuffer.Append(ProcessEscChar(ctx.L.inputChar)); 567 | ctx.NextState = ctx.StateStack; 568 | return true; 569 | default: 570 | return false; 571 | } 572 | } 573 | 574 | private static bool State22(FsmContext ctx) { 575 | int counter = 0; 576 | int mult = 4096; 577 | ctx.L.unichar = 0; 578 | while (ctx.L.GetChar()) { 579 | if (ctx.L.inputChar >= '0' && ctx.L.inputChar <= '9' || 580 | ctx.L.inputChar >= 'A' && ctx.L.inputChar <= 'F' || 581 | ctx.L.inputChar >= 'a' && ctx.L.inputChar <= 'f') { 582 | 583 | ctx.L.unichar += HexValue (ctx.L.inputChar) * mult; 584 | counter++; 585 | mult /= 16; 586 | if (counter == 4) { 587 | ctx.L.stringBuffer.Append(Convert.ToChar(ctx.L.unichar)); 588 | ctx.NextState = ctx.StateStack; 589 | return true; 590 | } 591 | continue; 592 | } 593 | return false; 594 | } 595 | return true; 596 | } 597 | 598 | private static bool State23(FsmContext ctx) { 599 | while (ctx.L.GetChar()) { 600 | switch (ctx.L.inputChar) { 601 | case '\'': 602 | ctx.L.UngetChar(); 603 | ctx.Return = true; 604 | ctx.NextState = 24; 605 | return true; 606 | case '\\': 607 | ctx.StateStack = 23; 608 | ctx.NextState = 21; 609 | return true; 610 | default: 611 | ctx.L.stringBuffer.Append((char)ctx.L.inputChar); 612 | continue; 613 | } 614 | } 615 | return true; 616 | } 617 | 618 | private static bool State24(FsmContext ctx) { 619 | ctx.L.GetChar(); 620 | switch (ctx.L.inputChar) { 621 | case '\'': 622 | ctx.L.inputChar = '"'; 623 | ctx.Return = true; 624 | ctx.NextState = 1; 625 | return true; 626 | default: 627 | return false; 628 | } 629 | } 630 | 631 | private static bool State25(FsmContext ctx) { 632 | ctx.L.GetChar(); 633 | switch (ctx.L.inputChar) { 634 | case '*': 635 | ctx.NextState = 27; 636 | return true; 637 | case '/': 638 | ctx.NextState = 26; 639 | return true; 640 | default: 641 | return false; 642 | } 643 | } 644 | 645 | private static bool State26(FsmContext ctx) { 646 | while (ctx.L.GetChar()) { 647 | if (ctx.L.inputChar == '\n') { 648 | ctx.NextState = 1; 649 | return true; 650 | } 651 | } 652 | return true; 653 | } 654 | 655 | private static bool State27(FsmContext ctx) { 656 | while (ctx.L.GetChar()) { 657 | if (ctx.L.inputChar == '*') { 658 | ctx.NextState = 28; 659 | return true; 660 | } 661 | } 662 | return true; 663 | } 664 | 665 | private static bool State28(FsmContext ctx) { 666 | while (ctx.L.GetChar()) { 667 | if (ctx.L.inputChar == '*') { 668 | continue; 669 | } 670 | if (ctx.L.inputChar == '/') { 671 | ctx.NextState = 1; 672 | return true; 673 | } 674 | ctx.NextState = 27; 675 | return true; 676 | } 677 | return true; 678 | } 679 | 680 | private bool GetChar() { 681 | if ((inputChar = NextChar()) != -1) { 682 | return true; 683 | } 684 | EndOfInput = true; 685 | return false; 686 | } 687 | 688 | private int NextChar() { 689 | if (inputBuffer != 0) { 690 | int tmp = inputBuffer; 691 | inputBuffer = 0; 692 | return tmp; 693 | } 694 | return reader.Read(); 695 | } 696 | 697 | public bool NextToken() { 698 | StateHandler handler; 699 | context.Return = false; 700 | while (true) { 701 | handler = handlerTable[state - 1]; 702 | if (!handler(context)) { 703 | throw new JsonException(inputChar); 704 | } 705 | if (EndOfInput) { 706 | return false; 707 | } 708 | if (context.Return) { 709 | StringValue = stringBuffer.ToString(); 710 | stringBuffer.Remove(0, stringBuffer.Length); 711 | Token = returnTable[state - 1]; 712 | if (Token == (int)ParserToken.Char) { 713 | Token = inputChar; 714 | } 715 | state = context.NextState; 716 | return true; 717 | } 718 | state = context.NextState; 719 | } 720 | } 721 | 722 | private void UngetChar() { 723 | inputBuffer = inputChar; 724 | } 725 | } 726 | 727 | } 728 | -------------------------------------------------------------------------------- /Source/ParserToken.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /* 3 | * The authors disclaim copyright to this source code. 4 | * For more details, see the COPYING file included with this distribution. 5 | */ 6 | #endregion 7 | 8 | namespace LitJson { 9 | 10 | /// 11 | /// Internal representation of the tokens used by the lexer and the parser. 12 | /// 13 | internal enum ParserToken { 14 | // Lexer tokens (see section A.1.1. of the manual) 15 | None = System.Char.MaxValue + 1, 16 | Number, 17 | True, 18 | False, 19 | Null, 20 | CharSeq, 21 | // Single char 22 | Char, 23 | // Parser Rules (see section A.2.1 of the manual) 24 | Text, 25 | Object, 26 | ObjectPrime, 27 | Pair, 28 | PairRest, 29 | Array, 30 | ArrayPrime, 31 | Value, 32 | ValueRest, 33 | String, 34 | // End of input 35 | End, 36 | // The empty rule 37 | Epsilon 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Source/Platform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using System.Linq; 6 | using System.IO; 7 | 8 | #if JSON_WINRT || (UNITY_METRO && !UNITY_EDITOR) 9 | namespace LitJson { 10 | 11 | public interface IOrderedDictionary : IDictionary, ICollection, IEnumerable { 12 | object this [int index] { 13 | get; 14 | set; 15 | } 16 | new IDictionaryEnumerator GetEnumerator (); 17 | void Insert (int index, object key, object value); 18 | void RemoveAt (int index); 19 | } 20 | 21 | public static class Platform { 22 | 23 | public static Type GetInterface(this Type _type,string name){ 24 | foreach(Type t in _type.GetTypeInfo().ImplementedInterfaces){ 25 | if(t.Name == name){ 26 | return t; 27 | } 28 | } 29 | return null; 30 | } 31 | 32 | public static FieldInfo[] GetFields(this Type _type){ 33 | return _type.GetTypeInfo().DeclaredFields.ToArray(); 34 | } 35 | 36 | // No way to patch BindingFlags... 37 | public static FieldInfo[] GetFields(this Type _type,BindingFlags flags){ 38 | return _type.GetTypeInfo().DeclaredFields.ToArray(); 39 | } 40 | 41 | public static PropertyInfo[] GetProperties(this Type _type){ 42 | return _type.GetTypeInfo().DeclaredProperties.ToArray(); 43 | } 44 | 45 | // No way to patch BindingFlags... 46 | public static PropertyInfo[] GetProperties(this Type _type,BindingFlags flags){ 47 | return _type.GetTypeInfo().DeclaredProperties.ToArray(); 48 | } 49 | 50 | public static MethodInfo GetMethod(this Type _type,string name, Type[] types){ 51 | return _type.GetRuntimeMethod(name,types); 52 | } 53 | 54 | public static bool IsAssignableFrom(this Type _type,Type other){ 55 | return _type.GetTypeInfo().IsAssignableFrom(other.GetTypeInfo()); 56 | } 57 | 58 | public static Type[] GetGenericArguments(this Type _type){ 59 | return _type.GetTypeInfo().GenericTypeArguments; 60 | } 61 | 62 | public static object[] GetCustomAttributes(this Type _type, Type attributeType, bool inherit){ 63 | return _type.GetTypeInfo().GetCustomAttributes(attributeType,inherit).ToArray(); 64 | } 65 | 66 | public static ConstructorInfo GetConstructor(this Type _type, BindingFlags bindingAttr, object binder, Type[] types, object[] modifiers){ 67 | return _type.GetTypeInfo().DeclaredConstructors.Where(c=>c.GetParameters().Count() == 0).Select(c=>c).FirstOrDefault(); 68 | } 69 | 70 | // Replace with extention properties if they are ever added to .net 71 | public static bool IsClass(this Type _type){ 72 | return _type.GetTypeInfo().IsClass; 73 | } 74 | 75 | // Replace with extention properties if they are ever added to .net 76 | public static bool IsEnum(this Type _type){ 77 | return _type.GetTypeInfo().IsEnum; 78 | } 79 | 80 | public static MethodInfo GetGetMethod(this PropertyInfo property ){ 81 | return property.GetMethod; 82 | } 83 | 84 | public static MethodInfo GetSetMethod(this PropertyInfo property ){ 85 | return property.SetMethod; 86 | } 87 | 88 | public static void Close(this TextReader _reader){ 89 | _reader.Dispose(); 90 | } 91 | 92 | } 93 | } 94 | #endif 95 | -------------------------------------------------------------------------------- /Source/Unity/UnityTypeBindings.cs: -------------------------------------------------------------------------------- 1 | #if !JSON_STANDALONE 2 | 3 | using UnityEngine; 4 | using System; 5 | using System.Collections; 6 | 7 | using LitJson.Extensions; 8 | 9 | namespace LitJson { 10 | 11 | #if UNITY_EDITOR 12 | [UnityEditor.InitializeOnLoad] 13 | #endif 14 | public static class UnityTypeBindings { 15 | 16 | static bool registerd; 17 | 18 | static UnityTypeBindings(){ 19 | Register(); 20 | } 21 | 22 | public static void Register(){ 23 | 24 | if(registerd) return; 25 | registerd = true; 26 | 27 | // If you seralize using WriteProperty() 28 | // LitJson will attempt to bind property 29 | // names to class members instead of using 30 | // an importer. 31 | 32 | // -- Type 33 | JsonMapper.RegisterExporter((v,w) => { 34 | w.Write(v.FullName); 35 | }); 36 | 37 | JsonMapper.RegisterImporter((s) => { 38 | return Type.GetType(s); 39 | }); 40 | 41 | // -- Vector2 42 | Action writeVector2 = (v,w) => { 43 | w.WriteObjectStart(); 44 | w.WriteProperty("x",v.x); 45 | w.WriteProperty("y",v.y); 46 | w.WriteObjectEnd(); 47 | }; 48 | 49 | JsonMapper.RegisterExporter((v,w) => { 50 | writeVector2(v,w); 51 | }); 52 | 53 | // -- Vector3 54 | Action writeVector3 = (v,w) => { 55 | w.WriteObjectStart(); 56 | w.WriteProperty("x",v.x); 57 | w.WriteProperty("y",v.y); 58 | w.WriteProperty("z",v.z); 59 | w.WriteObjectEnd(); 60 | }; 61 | 62 | JsonMapper.RegisterExporter((v,w) => { 63 | writeVector3(v,w); 64 | }); 65 | 66 | // -- Vector4 67 | JsonMapper.RegisterExporter((v,w) => { 68 | w.WriteObjectStart(); 69 | w.WriteProperty("x",v.x); 70 | w.WriteProperty("y",v.y); 71 | w.WriteProperty("z",v.z); 72 | w.WriteProperty("w",v.w); 73 | w.WriteObjectEnd(); 74 | }); 75 | 76 | // -- Quaternion 77 | JsonMapper.RegisterExporter((v,w) => { 78 | w.WriteObjectStart(); 79 | w.WriteProperty("x",v.x); 80 | w.WriteProperty("y",v.y); 81 | w.WriteProperty("z",v.z); 82 | w.WriteProperty("w",v.w); 83 | w.WriteObjectEnd(); 84 | }); 85 | 86 | // -- Color 87 | JsonMapper.RegisterExporter((v,w) => { 88 | w.WriteObjectStart(); 89 | w.WriteProperty("r",v.r); 90 | w.WriteProperty("g",v.g); 91 | w.WriteProperty("b",v.b); 92 | w.WriteProperty("a",v.a); 93 | w.WriteObjectEnd(); 94 | }); 95 | 96 | // -- Color32 97 | JsonMapper.RegisterExporter((v,w) => { 98 | w.WriteObjectStart(); 99 | w.WriteProperty("r",v.r); 100 | w.WriteProperty("g",v.g); 101 | w.WriteProperty("b",v.b); 102 | w.WriteProperty("a",v.a); 103 | w.WriteObjectEnd(); 104 | }); 105 | 106 | // -- Bounds 107 | JsonMapper.RegisterExporter((v,w) => { 108 | w.WriteObjectStart(); 109 | 110 | w.WritePropertyName("center"); 111 | writeVector3(v.center,w); 112 | 113 | w.WritePropertyName("size"); 114 | writeVector3(v.size,w); 115 | 116 | w.WriteObjectEnd(); 117 | }); 118 | 119 | // -- Rect 120 | JsonMapper.RegisterExporter((v,w) => { 121 | w.WriteObjectStart(); 122 | w.WriteProperty("x",v.x); 123 | w.WriteProperty("y",v.y); 124 | w.WriteProperty("width",v.width); 125 | w.WriteProperty("height",v.height); 126 | w.WriteObjectEnd(); 127 | }); 128 | 129 | // -- RectOffset 130 | JsonMapper.RegisterExporter((v,w) => { 131 | w.WriteObjectStart(); 132 | w.WriteProperty("top",v.top); 133 | w.WriteProperty("left",v.left); 134 | w.WriteProperty("bottom",v.bottom); 135 | w.WriteProperty("right",v.right); 136 | w.WriteObjectEnd(); 137 | }); 138 | 139 | } 140 | 141 | } 142 | } 143 | #endif 144 | -------------------------------------------------------------------------------- /UnityLitJson.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mervill/UnityLitJson/10d92bc9f06614eb7ebab26bc37c9acd920cf3cb/UnityLitJson.unitypackage -------------------------------------------------------------------------------- /benchmarks/.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exe 3 | *.out 4 | *.txt 5 | -------------------------------------------------------------------------------- /benchmarks/Benchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections; 4 | 5 | /// 6 | /// The attribute to use to mark methods as being 7 | /// the targets of benchmarking. 8 | /// 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class BenchmarkAttribute : Attribute 11 | { 12 | } 13 | 14 | /// 15 | /// Very simple benchmarking framework. Looks for all types 16 | /// in the current assembly which have public static parameterless 17 | /// methods marked with the Benchmark attribute. In addition, if 18 | /// there are public static Init, Reset and Check methods with 19 | /// appropriate parameters (a string array for Init, nothing for 20 | /// Reset or Check) these are called at appropriate times. 21 | /// 22 | public class Benchmark 23 | { 24 | /// 25 | /// Number of times to run the methods in each type. 26 | /// 27 | static int runIterations=1; 28 | 29 | public static void Main(string[] args) 30 | { 31 | args = ParseCommandLine (args); 32 | 33 | // Save all the benchmark classes from doing a nullity test 34 | if (args==null) 35 | args = new string[0]; 36 | 37 | // We're only ever interested in public static methods. This variable 38 | // just makes it easier to read the code... 39 | BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static; 40 | 41 | foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) 42 | { 43 | // Find an Init method taking string[], if any 44 | MethodInfo initMethod=type.GetMethod ("Init", publicStatic, null, 45 | new Type[]{typeof(string[])}, 46 | null); 47 | 48 | // Find a parameterless Reset method, if any 49 | MethodInfo resetMethod=type.GetMethod ("Reset", publicStatic, 50 | null, new Type[0], 51 | null); 52 | 53 | // Find a parameterless Check method, if any 54 | MethodInfo checkMethod=type.GetMethod("Check", publicStatic, 55 | null, new Type[0], 56 | null); 57 | 58 | // Find all parameterless methods with the [Benchmark] attribute 59 | ArrayList benchmarkMethods=new ArrayList(); 60 | foreach (MethodInfo method in type.GetMethods(publicStatic)) 61 | { 62 | ParameterInfo[] parameters = method.GetParameters(); 63 | if (parameters!=null && parameters.Length != 0) 64 | continue; 65 | 66 | if (method.GetCustomAttributes 67 | (typeof(BenchmarkAttribute), false).Length != 0) 68 | { 69 | benchmarkMethods.Add (method); 70 | } 71 | } 72 | 73 | // Ignore types with no appropriate methods to benchmark 74 | if (benchmarkMethods.Count==0) 75 | continue; 76 | 77 | Console.WriteLine ("Benchmarking type {0}", type.Name); 78 | 79 | // If we've got an Init method, call it once 80 | try 81 | { 82 | if (initMethod!=null) 83 | initMethod.Invoke (null, new object[]{args}); 84 | } 85 | catch (TargetInvocationException e) 86 | { 87 | Exception inner = e.InnerException; 88 | string message = (inner==null ? null : inner.Message); 89 | if (message==null) 90 | message = "(No message)"; 91 | Console.WriteLine ("Init failed ({0})", message); 92 | continue; // Next type 93 | } 94 | 95 | for (int i=0; i < runIterations; i++) 96 | { 97 | if (runIterations != 1) 98 | Console.WriteLine ("Run #{0}", i+1); 99 | 100 | foreach (MethodInfo method in benchmarkMethods) 101 | { 102 | try 103 | { 104 | // Reset (if appropriate) 105 | if (resetMethod!=null) 106 | resetMethod.Invoke(null, null); 107 | 108 | // Give the test as good a chance as possible 109 | // of avoiding garbage collection 110 | GC.Collect(); 111 | GC.WaitForPendingFinalizers(); 112 | GC.Collect(); 113 | 114 | // Now run the test itself 115 | DateTime start = DateTime.Now; 116 | method.Invoke (null, null); 117 | DateTime end = DateTime.Now; 118 | 119 | // Check the results (if appropriate) 120 | // Note that this doesn't affect the timing 121 | if (checkMethod!=null) 122 | checkMethod.Invoke(null, null); 123 | 124 | // If everything's worked, report the time taken, 125 | // nicely lined up (assuming no very long method names!) 126 | Console.WriteLine (" {0,-20} {1}", method.Name, end-start); 127 | } 128 | catch (TargetInvocationException e) 129 | { 130 | Exception inner = e.InnerException; 131 | string message = (inner==null ? null : inner.Message); 132 | if (message==null) 133 | message = "(No message)"; 134 | Console.WriteLine (" {0}: Failed ({1})", method.Name, message); 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | /// 142 | /// Parses the command line, returning an array of strings 143 | /// which are the arguments the tasks should receive. This 144 | /// array will definitely be non-null, even if null is 145 | /// passed in originally. 146 | /// 147 | static string[] ParseCommandLine (string[] args) 148 | { 149 | if (args==null) 150 | return new string[0]; 151 | 152 | for (int i=0; i < args.Length; i++) 153 | { 154 | switch (args[i]) 155 | { 156 | case "-runtwice": 157 | runIterations=2; 158 | break; 159 | 160 | case "-version": 161 | PrintEnvironment(); 162 | break; 163 | 164 | case "-endoptions": 165 | // All following options are for the benchmarked 166 | // types. 167 | { 168 | string[] ret = new string [args.Length-i-1]; 169 | Array.Copy (args, i+1, ret, 0, ret.Length); 170 | return ret; 171 | } 172 | 173 | default: 174 | // Don't understand option; copy this and 175 | // all remaining options and return them. 176 | { 177 | string[] ret = new string [args.Length-i]; 178 | Array.Copy (args, i, ret, 0, ret.Length); 179 | return ret; 180 | } 181 | } 182 | } 183 | // Understood all arguments 184 | return new string[0]; 185 | } 186 | 187 | /// 188 | /// Prints out information about the operating environment. 189 | /// 190 | static void PrintEnvironment() 191 | { 192 | Console.WriteLine ("Operating System: {0}", Environment.OSVersion); 193 | Console.WriteLine ("Runtime version: {0}", Environment.Version); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /benchmarks/BmCommon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | 5 | namespace LitJson.Benchmarks 6 | { 7 | public class Job 8 | { 9 | public string Title; 10 | public string Description; 11 | } 12 | 13 | public class Person 14 | { 15 | private int age; 16 | private double height; 17 | private Job job; 18 | private string name; 19 | private bool retired; 20 | private string[] urls; 21 | 22 | 23 | public int Age { 24 | get { return age; } 25 | set { age = value; } 26 | } 27 | 28 | public double Height { 29 | get { return height; } 30 | set { height = value; } 31 | } 32 | 33 | public Job Job { 34 | get { return job; } 35 | set { job = value; } 36 | } 37 | 38 | public string Name { 39 | get { return name; } 40 | set { name = value; } 41 | } 42 | 43 | public bool Retired { 44 | get { return retired; } 45 | set { retired = value; } 46 | } 47 | 48 | public string[] Urls { 49 | get { return urls; } 50 | set { urls = value; } 51 | } 52 | 53 | 54 | public override string ToString () 55 | { 56 | string url_list = String.Empty; 57 | 58 | foreach (string url in urls) 59 | url_list += "\n " + url; 60 | 61 | return String.Format ( 62 | "Hi, I'm {0}, {1} years old and {2} tall.\n" + 63 | "I'm {3}. I work as '{4}', where I {5}.\n" + 64 | "You can visit my websites at:{6}", 65 | name, age, height, 66 | retired ? "retired" : "not retired yet", 67 | job.Title, job.Description, url_list); 68 | } 69 | } 70 | 71 | 72 | public class Common 73 | { 74 | public static readonly int Iterations = 10000; 75 | 76 | // Originally the last couple of numbers didn't have the '.0' 77 | // portion, but Newtonsoft's library throws an Exception otherwise 78 | public static readonly string JsonNumbers = @" 79 | [ 80 | 42, 81 | 1, 82 | 1, 83 | 2, 84 | 3, 85 | 5, 86 | 8, 87 | -50, 88 | -678.56, 89 | 3.1415, 90 | 1.4e10, 91 | 4.0e5, 92 | 8.0e-3 93 | ] 94 | "; 95 | 96 | public static readonly string JsonStrings = @" 97 | [ 98 | ""Hello World!"", 99 | ""The quick brown fox jumps over the lazy dog"", 100 | ""Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."", 101 | ""$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^"" 102 | ] 103 | "; 104 | 105 | public static readonly string JsonText = @" 106 | 107 | { 108 | ""Image"": { 109 | ""FirstProperty"": true, 110 | ""Width"": 800, 111 | ""Height"": 600, 112 | ""Title"": ""View from 15th Floor"", 113 | ""Comment"": ""Sample text:\t\""abcdef\""\nSecond Line"", 114 | ""Comment2"": ""\u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1"", 115 | ""Default"": true, 116 | ""Active"": false, 117 | ""Resource"": null, 118 | ""Thumbnail"": { 119 | ""Url"": ""http://www.example.com/image/481989943"", 120 | ""Height"": 125, 121 | ""Width"": ""100"" }, 122 | ""IDs"": [ 116, 943, 234, 38793 ], 123 | ""Score"": 9.40, 124 | ""Scale"": 1.0e-1, 125 | ""Views"": 3000000000, 126 | ""LastProperty"": true 127 | } 128 | } 129 | 130 | "; 131 | 132 | public static readonly double[] SampleDoubles = new double[] { 133 | 0.0, 134 | 10.0, 135 | 3.1416, 136 | 0.0000001, 137 | -789.123, 138 | 0.00056, 139 | 50000000000.0 140 | }; 141 | 142 | public static readonly int[] SampleInts = new int[] { 143 | 0, 144 | 42, 145 | 100000, 146 | -1, 147 | -123, 148 | 7777, 149 | 25 150 | }; 151 | 152 | /* This is to be iterated by pairs. The first item is a char 153 | * indicating the type of the second item (if any): 154 | * 155 | * { - Object start 156 | * } - Object end 157 | * [ - Array start 158 | * ] - Array end 159 | * P - Property name 160 | * I - Int 161 | * D - Double 162 | * S - String 163 | * B - Boolean 164 | * N - Null 165 | */ 166 | public static readonly object[] SampleObject = new object[] { 167 | '[', null, 168 | '{', null, 169 | 'P', "precision", 170 | 'S', "zip", 171 | 'P', "Latitude", 172 | 'D', 37.7668, 173 | 'P', "Longitude", 174 | 'D', -122.3959, 175 | 'P', "Address", 176 | 'S', "", 177 | 'P', "City", 178 | 'S', "SAN FRANCISCO", 179 | 'P', "State", 180 | 'S', "CA", 181 | 'P', "Zip", 182 | 'S', "94107", 183 | 'P', "Country", 184 | 'S', "US", 185 | 'P', "Visited", 186 | 'B', true, 187 | 'P', "Ref", 188 | 'N', null, 189 | 'P', "Comment", 190 | 'S', "This is a \"comment\"\tColumn2\nLine2. " + 191 | "\u00c6nema is a good album.", 192 | '}', null, 193 | 194 | '{', null, 195 | 'P', "precision", 196 | 'S', "zip", 197 | 'P', "Latitude", 198 | 'D', 37.371991, 199 | 'P', "Longitude", 200 | 'D', -122.026020, 201 | 'P', "Address", 202 | 'S', "", 203 | 'P', "City", 204 | 'S', "SUNNYVALE", 205 | 'P', "State", 206 | 'S', "CA", 207 | 'P', "Zip", 208 | 'S', "94085", 209 | 'P', "Country", 210 | 'S', "US", 211 | 'P', "Visited", 212 | 'B', false, 213 | 'P', "Ref", 214 | 'N', null, 215 | '}', null, 216 | ']', null 217 | }; 218 | 219 | 220 | private static Person sample_person; 221 | 222 | public static Person SamplePerson { 223 | get { 224 | if (sample_person != null) 225 | return sample_person; 226 | 227 | sample_person = new Person (); 228 | 229 | sample_person.Name = "Art Vandelay"; 230 | sample_person.Age = 30; 231 | sample_person.Height = 1.65; 232 | sample_person.Retired = false; 233 | sample_person.Urls = new string[] { 234 | "http://example.com/artvandelay", 235 | "http://artvandelay.org/" 236 | }; 237 | 238 | sample_person.Job = new Job (); 239 | sample_person.Job.Title = "Importer/Exporter"; 240 | sample_person.Job.Description = "import matches... long matches"; 241 | 242 | return sample_person; 243 | } 244 | } 245 | 246 | public static readonly string PersonJson = @" 247 | 248 | { 249 | ""Name"" : ""Art Vandelay"", 250 | ""Age"" : 30, 251 | ""Height"" : 1.65, 252 | ""Retired"" : false, 253 | ""Urls"" : [ 254 | ""http://example.com/artvandelay"", 255 | ""http://artvandelay.org/"" ], 256 | 257 | ""Job"" : { 258 | ""Title"" : ""Importer/Exporter"", 259 | ""Description"" : ""import matches... long matches"" 260 | } 261 | } 262 | 263 | "; 264 | 265 | private static Hashtable hashtable_person; 266 | 267 | public static Hashtable HashtablePerson { 268 | get { 269 | if (hashtable_person != null) 270 | return hashtable_person; 271 | 272 | hashtable_person = new Hashtable (); 273 | hashtable_person.Add ("Name", "Art Vandelay"); 274 | hashtable_person.Add ("Age", 30); 275 | hashtable_person.Add ("Height", 1.65); 276 | hashtable_person.Add ("Retired", false); 277 | 278 | string[] urls = new string[] { 279 | "http://example.com/artvandelay", 280 | "http://artvandelay.org/" 281 | }; 282 | 283 | hashtable_person.Add ("Urls", urls); 284 | 285 | Hashtable job = new Hashtable (); 286 | job.Add ("Title", "Importer/Exporter"); 287 | job.Add ("Description", "import matches... long matches"); 288 | 289 | hashtable_person.Add ("Job", job); 290 | 291 | return hashtable_person; 292 | } 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /benchmarks/BmJayrockExport.cs: -------------------------------------------------------------------------------- 1 | using Jayrock.Json; 2 | using Jayrock.Json.Conversion; 3 | 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkJayrock 10 | { 11 | static JsonObject person; 12 | 13 | 14 | public static void Init (string[] args) 15 | { 16 | person = (JsonObject) JsonConvert.Import (Common.PersonJson); 17 | } 18 | 19 | [Benchmark] 20 | public static void JayrockConversionFromGenericObject () 21 | { 22 | for (int i = 0; i < Common.Iterations; i++) { 23 | string json = JsonConvert.ExportToString (person); 24 | 25 | TextWriter.Null.Write(json); 26 | } 27 | } 28 | 29 | [Benchmark] 30 | public static void JayrockConversionFromHashtable () 31 | { 32 | for (int i = 0; i < Common.Iterations; i++) { 33 | string json = JsonConvert.ExportToString ( 34 | Common.HashtablePerson); 35 | 36 | TextWriter.Null.Write(json); 37 | } 38 | } 39 | 40 | [Benchmark] 41 | public static void JayrockConversionFromObject () 42 | { 43 | for (int i = 0; i < Common.Iterations; i++) { 44 | string json = JsonConvert.ExportToString ( 45 | Common.SamplePerson); 46 | 47 | TextWriter.Null.Write(json); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /benchmarks/BmJayrockImport.cs: -------------------------------------------------------------------------------- 1 | using Jayrock.Json; 2 | using Jayrock.Json.Conversion; 3 | 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkJayrock 10 | { 11 | [Benchmark] 12 | public static void JayrockConversionToGenericObject () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | JsonObject art = (JsonObject) JsonConvert.Import ( 16 | Common.PersonJson); 17 | 18 | TextWriter.Null.Write(art["Name"]); 19 | } 20 | } 21 | 22 | [Benchmark] 23 | public static void JayrockConversionToObject () 24 | { 25 | for (int i = 0; i < Common.Iterations; i++) { 26 | Person art = (Person) JsonConvert.Import (typeof(Person), 27 | Common.PersonJson); 28 | 29 | TextWriter.Null.Write(art.ToString ()); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /benchmarks/BmJayrockOutput.cs: -------------------------------------------------------------------------------- 1 | using Jayrock.Json; 2 | using Jayrock.Json.Conversion; 3 | 4 | using System; 5 | using System.IO; 6 | using System.Text; 7 | 8 | 9 | namespace LitJson.Benchmarks 10 | { 11 | public class BenchmarkJayrock 12 | { 13 | [Benchmark] 14 | public static void JayrockOutputFile () 15 | { 16 | using (FileStream fs = new FileStream ("jayrock_out.txt", 17 | FileMode.Create)) { 18 | StreamWriter out_stream = new StreamWriter (fs); 19 | 20 | StringReader sr = new StringReader (Common.JsonText); 21 | 22 | JsonReader reader = new JsonTextReader (sr); 23 | 24 | 25 | out_stream.WriteLine ( 26 | "*** Reading with Jayrock.Json.JsonReader"); 27 | 28 | while (reader.Read()) { 29 | out_stream.Write("Token: {0}", reader.TokenClass); 30 | 31 | if (reader.Text != null) 32 | out_stream.WriteLine (" Value: {0}", reader.Text); 33 | else 34 | out_stream.WriteLine (""); 35 | 36 | } 37 | 38 | 39 | out_stream.WriteLine ( 40 | "\n*** Writing with Jayrock.Json.JsonWriter"); 41 | 42 | JsonWriter writer = new JsonTextWriter (out_stream); 43 | int n = Common.SampleObject.Length; 44 | for (int i = 0; i < n; i += 2) { 45 | switch ((char) Common.SampleObject[i]) { 46 | case '{': 47 | writer.WriteStartObject (); 48 | break; 49 | 50 | case '}': 51 | writer.WriteEndObject (); 52 | break; 53 | 54 | case '[': 55 | writer.WriteStartArray (); 56 | break; 57 | 58 | case ']': 59 | writer.WriteEndArray (); 60 | break; 61 | 62 | case 'P': 63 | writer.WriteMember ( 64 | (string) Common.SampleObject[i + 1]); 65 | break; 66 | 67 | case 'I': 68 | writer.WriteNumber ( 69 | (int) Common.SampleObject[i + 1]); 70 | break; 71 | 72 | case 'D': 73 | writer.WriteNumber ( 74 | (double) Common.SampleObject[i + 1]); 75 | break; 76 | 77 | case 'S': 78 | writer.WriteString ( 79 | (string) Common.SampleObject[i + 1]); 80 | break; 81 | 82 | case 'B': 83 | writer.WriteBoolean ( 84 | (bool) Common.SampleObject[i + 1]); 85 | break; 86 | 87 | case 'N': 88 | writer.WriteNull (); 89 | break; 90 | } 91 | } 92 | 93 | 94 | out_stream.WriteLine ( 95 | "\n\n*** Data imported with " + 96 | "Jayrock.Json.Conversion.JsonConvert\n"); 97 | 98 | Person art = (Person) JsonConvert.Import (typeof(Person), 99 | Common.PersonJson); 100 | 101 | out_stream.Write(art.ToString ()); 102 | 103 | 104 | out_stream.WriteLine ( 105 | "\n\n*** Object exported with " + 106 | "Jayrock.Json.Conversion.JsonConvert\n"); 107 | 108 | out_stream.Write(JsonConvert.ExportToString ( 109 | Common.SamplePerson)); 110 | 111 | out_stream.WriteLine ( 112 | "\n\n*** Generic object exported with " + 113 | "Jayrock.Json.Conversion.JsonConvert\n"); 114 | JsonObject person = (JsonObject) JsonConvert.Import ( 115 | Common.PersonJson); 116 | 117 | out_stream.Write(JsonConvert.ExportToString (person)); 118 | 119 | 120 | out_stream.WriteLine ( 121 | "\n\n*** Hashtable exported with " + 122 | "Jayrock.Json.Conversion.JsonConvert\n"); 123 | 124 | out_stream.Write(JsonConvert.ExportToString ( 125 | Common.HashtablePerson)); 126 | 127 | out_stream.Close (); 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /benchmarks/BmJayrockReader.cs: -------------------------------------------------------------------------------- 1 | using Jayrock.Json; 2 | 3 | using System; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkJayrock 10 | { 11 | [Benchmark] 12 | public static void JayrockReaderNumbers () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | StringReader sr = new StringReader (Common.JsonNumbers); 16 | JsonReader reader = new JsonTextReader (sr); 17 | 18 | while (reader.Read()); 19 | } 20 | } 21 | 22 | [Benchmark] 23 | public static void JayrockReaderStrings () 24 | { 25 | for (int i = 0; i < Common.Iterations; i++) { 26 | StringReader sr = new StringReader (Common.JsonStrings); 27 | JsonReader reader = new JsonTextReader (sr); 28 | 29 | while (reader.Read()); 30 | } 31 | } 32 | 33 | [Benchmark] 34 | public static void JayrockReaderFirstProperty () 35 | { 36 | for (int i = 0; i < Common.Iterations; i++) { 37 | bool found = false; 38 | 39 | StringReader sr = new StringReader (Common.JsonText); 40 | 41 | JsonReader reader = new JsonTextReader (sr); 42 | 43 | while (reader.Read()) { 44 | if (reader.TokenClass == JsonTokenClass.Member && 45 | reader.Text == "FirstProperty") { 46 | found = true; 47 | break; 48 | } 49 | } 50 | 51 | if (! found) 52 | Console.WriteLine ("FirstProperty not found!"); 53 | } 54 | } 55 | 56 | [Benchmark] 57 | public static void JayrockReaderLastProperty () 58 | { 59 | for (int i = 0; i < Common.Iterations; i++) { 60 | bool found = false; 61 | 62 | StringReader sr = new StringReader (Common.JsonText); 63 | 64 | JsonReader reader = new JsonTextReader (sr); 65 | 66 | while (reader.Read()) { 67 | if (reader.TokenClass == JsonTokenClass.Member && 68 | reader.Text == "LastProperty") { 69 | found = true; 70 | break; 71 | } 72 | } 73 | 74 | if (! found) 75 | Console.WriteLine ("LastProperty not found!"); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /benchmarks/BmJayrockWriter.cs: -------------------------------------------------------------------------------- 1 | using Jayrock.Json; 2 | 3 | using System; 4 | using System.IO; 5 | using System.Text; 6 | 7 | 8 | namespace LitJson.Benchmarks 9 | { 10 | public class BenchmarkJayrock 11 | { 12 | [Benchmark] 13 | public static void JayrockWriterNumbers () 14 | { 15 | for (int i = 0; i < Common.Iterations; i++) { 16 | StringBuilder output = new StringBuilder (); 17 | JsonWriter writer = new JsonTextWriter (new StringWriter (output)); 18 | 19 | writer.WriteStartArray (); 20 | 21 | foreach (int n in Common.SampleInts) 22 | writer.WriteNumber (n); 23 | 24 | foreach (double n in Common.SampleDoubles) 25 | writer.WriteNumber (n); 26 | 27 | writer.WriteEndArray (); 28 | } 29 | } 30 | 31 | [Benchmark] 32 | public static void JayrockWriterObjects () 33 | { 34 | for (int j = 0; j < Common.Iterations; j++) { 35 | StringBuilder output = new StringBuilder (); 36 | JsonWriter writer = new JsonTextWriter (new StringWriter (output)); 37 | 38 | int n = Common.SampleObject.Length; 39 | for (int i = 0; i < n; i += 2) { 40 | switch ((char) Common.SampleObject[i]) { 41 | case '{': 42 | writer.WriteStartObject (); 43 | break; 44 | 45 | case '}': 46 | writer.WriteEndObject (); 47 | break; 48 | 49 | case '[': 50 | writer.WriteStartArray (); 51 | break; 52 | 53 | case ']': 54 | writer.WriteEndArray (); 55 | break; 56 | 57 | case 'P': 58 | writer.WriteMember ( 59 | (string) Common.SampleObject[i + 1]); 60 | break; 61 | 62 | case 'I': 63 | writer.WriteNumber ( 64 | (int) Common.SampleObject[i + 1]); 65 | break; 66 | 67 | case 'D': 68 | writer.WriteNumber ( 69 | (double) Common.SampleObject[i + 1]); 70 | break; 71 | 72 | case 'S': 73 | writer.WriteString ( 74 | (string) Common.SampleObject[i + 1]); 75 | break; 76 | 77 | case 'B': 78 | writer.WriteBoolean ( 79 | (bool) Common.SampleObject[i + 1]); 80 | break; 81 | 82 | case 'N': 83 | writer.WriteNull (); 84 | break; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /benchmarks/BmLitJsonExport.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | 3 | using System.IO; 4 | 5 | 6 | namespace LitJson.Benchmarks 7 | { 8 | public class BenchmarkLitJson 9 | { 10 | static JsonData person; 11 | 12 | 13 | public static void Init (string[] args) 14 | { 15 | person = JsonMapper.ToObject (Common.PersonJson); 16 | } 17 | 18 | [Benchmark] 19 | public static void LitJsonConversionFromGenericObject () 20 | { 21 | for (int i = 0; i < Common.Iterations; i++) { 22 | string json = JsonMapper.ToJson(person); 23 | 24 | TextWriter.Null.Write(json); 25 | } 26 | } 27 | 28 | [Benchmark] 29 | public static void LitJsonConversionFromHashtable () 30 | { 31 | for (int i = 0; i < Common.Iterations; i++) { 32 | string json = JsonMapper.ToJson(Common.HashtablePerson); 33 | 34 | TextWriter.Null.Write(json); 35 | } 36 | } 37 | 38 | [Benchmark] 39 | public static void LitJsonConversionFromObject () 40 | { 41 | for (int i = 0; i < Common.Iterations; i++) { 42 | string json = JsonMapper.ToJson(Common.SamplePerson); 43 | 44 | TextWriter.Null.Write(json); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /benchmarks/BmLitJsonImport.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | 3 | using System.Collections; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkLitJson 10 | { 11 | [Benchmark] 12 | public static void LitJsonConversionToGenericObject () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | JsonData art = JsonMapper.ToObject (Common.PersonJson); 16 | 17 | TextWriter.Null.Write(art["Name"]); 18 | } 19 | } 20 | 21 | [Benchmark] 22 | public static void LitJsonConversionToHashtable () 23 | { 24 | for (int i = 0; i < Common.Iterations; i++) { 25 | Hashtable art = JsonMapper.ToObject ( 26 | Common.PersonJson); 27 | 28 | TextWriter.Null.Write(art["Name"]); 29 | } 30 | } 31 | 32 | [Benchmark] 33 | public static void LitJsonConversionToObject () 34 | { 35 | for (int i = 0; i < Common.Iterations; i++) { 36 | Person art = JsonMapper.ToObject (Common.PersonJson); 37 | 38 | TextWriter.Null.Write(art.ToString ()); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /benchmarks/BmLitJsonOutput.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | 3 | using System.IO; 4 | 5 | 6 | namespace LitJson.Benchmarks 7 | { 8 | public class BenchmarkLitJson 9 | { 10 | [Benchmark] 11 | public static void LitJsonOutputFile () 12 | { 13 | using (FileStream fs = new FileStream ("litjson_out.txt", 14 | FileMode.Create)) { 15 | StreamWriter out_stream = new StreamWriter (fs); 16 | 17 | JsonReader reader = new JsonReader (Common.JsonText); 18 | 19 | out_stream.WriteLine ("*** Reading with LitJson.JsonReader"); 20 | 21 | while (reader.Read()) { 22 | out_stream.Write("Token: {0}", reader.Token); 23 | 24 | if (reader.Value != null) 25 | out_stream.WriteLine (" Value: {0}", reader.Value); 26 | else 27 | out_stream.WriteLine (""); 28 | } 29 | 30 | 31 | out_stream.WriteLine ( 32 | "\n*** Writing with LitJson.JsonWriter"); 33 | 34 | JsonWriter writer = new JsonWriter(out_stream); 35 | int n = Common.SampleObject.Length; 36 | for (int i = 0; i < n; i += 2) { 37 | switch ((char) Common.SampleObject[i]) { 38 | case '{': 39 | writer.WriteObjectStart(); 40 | break; 41 | 42 | case '}': 43 | writer.WriteObjectEnd(); 44 | break; 45 | 46 | case '[': 47 | writer.WriteArrayStart(); 48 | break; 49 | 50 | case ']': 51 | writer.WriteArrayEnd(); 52 | break; 53 | 54 | case 'P': 55 | writer.WritePropertyName( 56 | (string) Common.SampleObject[i + 1]); 57 | break; 58 | 59 | case 'I': 60 | writer.Write( 61 | (int) Common.SampleObject[i + 1]); 62 | break; 63 | 64 | case 'D': 65 | writer.Write( 66 | (double) Common.SampleObject[i + 1]); 67 | break; 68 | 69 | case 'S': 70 | writer.Write( 71 | (string) Common.SampleObject[i + 1]); 72 | break; 73 | 74 | case 'B': 75 | writer.Write( 76 | (bool) Common.SampleObject[i + 1]); 77 | break; 78 | 79 | case 'N': 80 | writer.Write(null); 81 | break; 82 | } 83 | } 84 | 85 | 86 | out_stream.WriteLine ( 87 | "\n\n*** Data imported with " + 88 | "LitJson.JsonMapper\n"); 89 | 90 | Person art = JsonMapper.ToObject (Common.PersonJson); 91 | 92 | out_stream.Write(art.ToString ()); 93 | 94 | 95 | out_stream.WriteLine ( 96 | "\n\n*** Object exported with " + 97 | "LitJson.JsonMapper\n"); 98 | 99 | out_stream.Write(JsonMapper.ToJson(Common.SamplePerson)); 100 | 101 | 102 | out_stream.WriteLine ( 103 | "\n\n*** Generic object exported with " + 104 | "LitJson.JsonMapper\n"); 105 | 106 | JsonData person = JsonMapper.ToObject (Common.PersonJson); 107 | 108 | out_stream.Write(JsonMapper.ToJson(person)); 109 | 110 | 111 | out_stream.WriteLine ( 112 | "\n\n*** Hashtable exported with " + 113 | "LitJson.JsonMapper\n"); 114 | 115 | out_stream.Write(JsonMapper.ToJson(Common.HashtablePerson)); 116 | 117 | out_stream.Close (); 118 | } 119 | } 120 | 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /benchmarks/BmLitJsonReader.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | 3 | using System; 4 | 5 | 6 | namespace LitJson.Benchmarks 7 | { 8 | public class BenchmarkLitJson 9 | { 10 | [Benchmark] 11 | public static void LitJsonReaderNumbers () 12 | { 13 | for (int i = 0; i < Common.Iterations; i++) { 14 | JsonReader reader = new JsonReader (Common.JsonNumbers); 15 | 16 | while (reader.Read()); 17 | } 18 | } 19 | 20 | [Benchmark] 21 | public static void LitJsonReaderStrings () 22 | { 23 | for (int i = 0; i < Common.Iterations; i++) { 24 | JsonReader reader = new JsonReader (Common.JsonStrings); 25 | 26 | while (reader.Read()); 27 | } 28 | } 29 | 30 | [Benchmark] 31 | public static void LitJsonReaderFirstProperty () 32 | { 33 | for (int i = 0; i < Common.Iterations; i++) { 34 | bool found = false; 35 | 36 | JsonReader reader = new JsonReader (Common.JsonText); 37 | 38 | while (reader.Read()) { 39 | if (reader.Token == JsonToken.PropertyName && 40 | (string) reader.Value == "FirstProperty") { 41 | found = true; 42 | break; 43 | } 44 | } 45 | 46 | if (! found) 47 | Console.WriteLine ("FirstProperty not found!"); 48 | } 49 | } 50 | 51 | [Benchmark] 52 | public static void LitJsonReaderLastProperty () 53 | { 54 | for (int i = 0; i < Common.Iterations; i++) { 55 | bool found = false; 56 | 57 | JsonReader reader = new JsonReader (Common.JsonText); 58 | 59 | while (reader.Read()) { 60 | if (reader.Token == JsonToken.PropertyName && 61 | (string) reader.Value == "LastProperty") { 62 | found = true; 63 | 64 | break; 65 | } 66 | } 67 | 68 | if (! found) 69 | Console.WriteLine ("FirstProperty not found!"); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /benchmarks/BmLitJsonWriter.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | 3 | using System.IO; 4 | using System.Text; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkLitJson 10 | { 11 | [Benchmark] 12 | public static void LitJsonWriterNumbers () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | StringBuilder output = new StringBuilder (); 16 | JsonWriter writer = new JsonWriter(new StringWriter (output)); 17 | 18 | writer.WriteArrayStart(); 19 | 20 | foreach (int n in Common.SampleInts) 21 | writer.Write(n); 22 | 23 | foreach (double n in Common.SampleDoubles) 24 | writer.Write(n); 25 | 26 | writer.WriteArrayEnd(); 27 | } 28 | } 29 | 30 | [Benchmark] 31 | public static void LitJsonWriterObjects () 32 | { 33 | for (int j = 0; j < Common.Iterations; j++) { 34 | StringBuilder output = new StringBuilder (); 35 | JsonWriter writer = new JsonWriter(new StringWriter (output)); 36 | 37 | int n = Common.SampleObject.Length; 38 | for (int i = 0; i < n; i += 2) { 39 | switch ((char) Common.SampleObject[i]) { 40 | case '{': 41 | writer.WriteObjectStart(); 42 | break; 43 | 44 | case '}': 45 | writer.WriteObjectEnd(); 46 | break; 47 | 48 | case '[': 49 | writer.WriteArrayStart(); 50 | break; 51 | 52 | case ']': 53 | writer.WriteArrayEnd(); 54 | break; 55 | 56 | case 'P': 57 | writer.WritePropertyName( 58 | (string) Common.SampleObject[i + 1]); 59 | break; 60 | 61 | case 'I': 62 | writer.Write( 63 | (int) Common.SampleObject[i + 1]); 64 | break; 65 | 66 | case 'D': 67 | writer.Write( 68 | (double) Common.SampleObject[i + 1]); 69 | break; 70 | 71 | case 'S': 72 | writer.Write( 73 | (string) Common.SampleObject[i + 1]); 74 | break; 75 | 76 | case 'B': 77 | writer.Write( 78 | (bool) Common.SampleObject[i + 1]); 79 | break; 80 | 81 | case 'N': 82 | writer.Write(null); 83 | break; 84 | } 85 | } 86 | 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /benchmarks/BmNewtonsoftExport.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using System.Collections; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkNewtonsoft 10 | { 11 | static JavaScriptObject person; 12 | 13 | 14 | public static void Init (string[] args) 15 | { 16 | person = (JavaScriptObject) 17 | JavaScriptConvert.DeserializeObject ( 18 | Common.PersonJson, typeof(JavaScriptObject)); 19 | } 20 | 21 | [Benchmark] 22 | public static void NewtonsoftConversionFromGenericObject () 23 | { 24 | for (int i = 0; i < Common.Iterations; i++) { 25 | string json = JavaScriptConvert.SerializeObject (person); 26 | 27 | TextWriter.Null.Write(json); 28 | } 29 | } 30 | 31 | [Benchmark] 32 | public static void NewtonsoftConversionFromHashtable () 33 | { 34 | for (int i = 0; i < Common.Iterations; i++) { 35 | string json = JavaScriptConvert.SerializeObject ( 36 | Common.HashtablePerson); 37 | 38 | TextWriter.Null.Write(json); 39 | } 40 | } 41 | 42 | [Benchmark] 43 | public static void NewtonsoftConversionFromObject () 44 | { 45 | for (int i = 0; i < Common.Iterations; i++) { 46 | string json = JavaScriptConvert.SerializeObject ( 47 | Common.SamplePerson); 48 | 49 | TextWriter.Null.Write(json); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /benchmarks/BmNewtonsoftImport.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using System.Collections; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkNewtonsoft 10 | { 11 | [Benchmark] 12 | public static void NewtonsoftConversionToGenericObject () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | JavaScriptObject art = (JavaScriptObject) 16 | JavaScriptConvert.DeserializeObject ( 17 | Common.PersonJson, typeof(JavaScriptObject)); 18 | 19 | TextWriter.Null.Write(art["Name"]); 20 | } 21 | } 22 | 23 | [Benchmark] 24 | public static void NewtonsoftConversionToHashtable () 25 | { 26 | for (int i = 0; i < Common.Iterations; i++) { 27 | Hashtable art = (Hashtable) 28 | JavaScriptConvert.DeserializeObject ( 29 | Common.PersonJson, typeof(Hashtable)); 30 | 31 | TextWriter.Null.Write(art["Name"]); 32 | } 33 | } 34 | 35 | [Benchmark] 36 | public static void NewtonsoftConversionToObject () 37 | { 38 | for (int i = 0; i < Common.Iterations; i++) { 39 | Person art = (Person) 40 | JavaScriptConvert.DeserializeObject ( 41 | Common.PersonJson, typeof(Person)); 42 | 43 | TextWriter.Null.Write(art.ToString ()); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /benchmarks/BmNewtonsoftOutput.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using System; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkNewtonsoft 10 | { 11 | [Benchmark] 12 | public static void NewtonsoftOutputFile () 13 | { 14 | using (FileStream fs = new FileStream ("newtonsoft_out.txt", 15 | FileMode.Create)) { 16 | StreamWriter out_stream = new StreamWriter (fs); 17 | 18 | StringReader sr = new StringReader (Common.JsonText); 19 | 20 | JsonReader reader = new JsonReader (sr); 21 | 22 | out_stream.WriteLine ( 23 | "*** Reading with Newtonsoft.Json.JsonReader"); 24 | 25 | int count = 0; 26 | 27 | while (reader.Read()) { 28 | out_stream.Write("Token: {0}", reader.TokenType); 29 | 30 | if (reader.Value != null) 31 | out_stream.WriteLine (" Value: {0}", reader.Value); 32 | else 33 | out_stream.WriteLine (""); 34 | 35 | 36 | // reader.Read() loops instead of returning false at the end 37 | if (reader.TokenType == JsonToken.EndObject) 38 | count++; 39 | 40 | if (count == 3) 41 | break; 42 | } 43 | 44 | 45 | out_stream.WriteLine ( 46 | "\n*** Writing with Newtonsoft.Json.JsonWriter"); 47 | 48 | JsonWriter writer = new JsonWriter(out_stream); 49 | int n = Common.SampleObject.Length; 50 | for (int i = 0; i < n; i += 2) { 51 | switch ((char) Common.SampleObject[i]) { 52 | case '{': 53 | writer.WriteStartObject (); 54 | break; 55 | 56 | case '}': 57 | writer.WriteEndObject (); 58 | break; 59 | 60 | case '[': 61 | writer.WriteStartArray (); 62 | break; 63 | 64 | case ']': 65 | writer.WriteEndArray (); 66 | break; 67 | 68 | case 'P': 69 | writer.WritePropertyName( 70 | (string) Common.SampleObject[i + 1]); 71 | break; 72 | 73 | case 'I': 74 | writer.WriteValue ( 75 | (int) Common.SampleObject[i + 1]); 76 | break; 77 | 78 | case 'D': 79 | writer.WriteValue ( 80 | (double) Common.SampleObject[i + 1]); 81 | break; 82 | 83 | case 'S': 84 | writer.WriteValue ( 85 | (string) Common.SampleObject[i + 1]); 86 | break; 87 | 88 | case 'B': 89 | writer.WriteValue ( 90 | (bool) Common.SampleObject[i + 1]); 91 | break; 92 | 93 | case 'N': 94 | writer.WriteNull (); 95 | break; 96 | } 97 | } 98 | 99 | 100 | out_stream.WriteLine ( 101 | "\n\n*** Data imported with " + 102 | "Newtonsoft.Json.JavaScriptConvert\n"); 103 | 104 | Person art = (Person) 105 | JavaScriptConvert.DeserializeObject ( 106 | Common.PersonJson, typeof(Person)); 107 | 108 | out_stream.Write(art.ToString ()); 109 | 110 | 111 | out_stream.WriteLine ( 112 | "\n\n*** Object exported with " + 113 | "Newtonsoft.Json.JavaScriptConvert\n"); 114 | 115 | out_stream.Write(JavaScriptConvert.SerializeObject ( 116 | Common.SamplePerson)); 117 | 118 | 119 | out_stream.WriteLine ( 120 | "\n\n*** Generic object exported with " + 121 | "Newtonsoft.Json.JavaScriptConvert\n"); 122 | 123 | JavaScriptObject person = (JavaScriptObject) 124 | JavaScriptConvert.DeserializeObject ( 125 | Common.PersonJson, typeof(JavaScriptObject)); 126 | 127 | out_stream.Write(JavaScriptConvert.SerializeObject (person)); 128 | 129 | 130 | out_stream.WriteLine ( 131 | "\n\n*** Hashtable exported with " + 132 | "Newtonsoft.Json.JavaScriptConvert\n"); 133 | 134 | out_stream.Write(JavaScriptConvert.SerializeObject ( 135 | Common.HashtablePerson)); 136 | 137 | out_stream.Close (); 138 | } 139 | } 140 | 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /benchmarks/BmNewtonsoftReader.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using System; 4 | using System.IO; 5 | 6 | 7 | namespace LitJson.Benchmarks 8 | { 9 | public class BenchmarkNewtonsoft 10 | { 11 | [Benchmark] 12 | public static void NewtonsoftReaderNumbers () 13 | { 14 | for (int i = 0; i < Common.Iterations; i++) { 15 | StringReader sr = new StringReader (Common.JsonNumbers); 16 | JsonReader reader = new JsonReader (sr); 17 | 18 | while (reader.Read() && 19 | reader.TokenType != JsonToken.EndArray); 20 | } 21 | } 22 | 23 | [Benchmark] 24 | public static void NewtonsoftReaderStrings () 25 | { 26 | for (int i = 0; i < Common.Iterations; i++) { 27 | StringReader sr = new StringReader (Common.JsonStrings); 28 | JsonReader reader = new JsonReader (sr); 29 | 30 | while (reader.Read() && 31 | reader.TokenType != JsonToken.EndArray); 32 | } 33 | } 34 | 35 | [Benchmark] 36 | public static void NewtonsoftReaderFirstProperty () 37 | { 38 | for (int i = 0; i < Common.Iterations; i++) { 39 | bool found = false; 40 | 41 | StringReader sr = new StringReader (Common.JsonText); 42 | 43 | JsonReader reader = new JsonReader (sr); 44 | 45 | while (reader.Read()) { 46 | if (reader.TokenType == JsonToken.PropertyName && 47 | (string) reader.Value == "FirstProperty") { 48 | found = true; 49 | break; 50 | } 51 | } 52 | 53 | if (! found) 54 | Console.WriteLine ("FirstProperty not found!"); 55 | } 56 | } 57 | 58 | [Benchmark] 59 | public static void NewtonsoftReaderLastProperty () 60 | { 61 | for (int i = 0; i < Common.Iterations; i++) { 62 | bool found = false; 63 | 64 | StringReader sr = new StringReader (Common.JsonText); 65 | 66 | JsonReader reader = new JsonReader (sr); 67 | 68 | while (reader.Read()) { 69 | if (reader.TokenType == JsonToken.PropertyName && 70 | (string) reader.Value == "LastProperty") { 71 | found = true; 72 | break; 73 | } 74 | } 75 | 76 | if (! found) 77 | Console.WriteLine ("LastProperty not found!"); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /benchmarks/BmNewtonsoftWriter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | using System; 4 | using System.IO; 5 | using System.Text; 6 | 7 | 8 | namespace LitJson.Benchmarks 9 | { 10 | public class BenchmarkNewtonsoft 11 | { 12 | [Benchmark] 13 | public static void NewtonsoftWriterNumbers () 14 | { 15 | for (int i = 0; i < Common.Iterations; i++) { 16 | StringBuilder output = new StringBuilder (); 17 | JsonWriter writer = new JsonWriter(new StringWriter (output)); 18 | 19 | writer.WriteStartArray (); 20 | 21 | foreach (int n in Common.SampleInts) 22 | writer.WriteValue (n); 23 | 24 | foreach (double n in Common.SampleDoubles) 25 | writer.WriteValue (n); 26 | 27 | writer.WriteEndArray (); 28 | } 29 | } 30 | 31 | [Benchmark] 32 | public static void NewtonsoftWriterObjects () 33 | { 34 | for (int j = 0; j < Common.Iterations; j++) { 35 | StringBuilder output = new StringBuilder (); 36 | JsonWriter writer = new JsonWriter(new StringWriter (output)); 37 | 38 | int n = Common.SampleObject.Length; 39 | for (int i = 0; i < n; i += 2) { 40 | switch ((char) Common.SampleObject[i]) { 41 | case '{': 42 | writer.WriteStartObject (); 43 | break; 44 | 45 | case '}': 46 | writer.WriteEndObject (); 47 | break; 48 | 49 | case '[': 50 | writer.WriteStartArray (); 51 | break; 52 | 53 | case ']': 54 | writer.WriteEndArray (); 55 | break; 56 | 57 | case 'P': 58 | writer.WritePropertyName( 59 | (string) Common.SampleObject[i + 1]); 60 | break; 61 | 62 | case 'I': 63 | writer.WriteValue ( 64 | (int) Common.SampleObject[i + 1]); 65 | break; 66 | 67 | case 'D': 68 | writer.WriteValue ( 69 | (double) Common.SampleObject[i + 1]); 70 | break; 71 | 72 | case 'S': 73 | writer.WriteValue ( 74 | (string) Common.SampleObject[i + 1]); 75 | break; 76 | 77 | case 'B': 78 | writer.WriteValue ( 79 | (bool) Common.SampleObject[i + 1]); 80 | break; 81 | 82 | case 'N': 83 | writer.WriteNull (); 84 | break; 85 | } 86 | } 87 | 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /benchmarks/GNUmakefile: -------------------------------------------------------------------------------- 1 | BENCHMARKS_JAYROCK = \ 2 | BmJayrockReader.exe \ 3 | BmJayrockWriter.exe \ 4 | BmJayrockImport.exe \ 5 | BmJayrockExport.exe \ 6 | BmJayrockOutput.exe 7 | 8 | BENCHMARKS_LITJSON = \ 9 | BmLitJsonReader.exe \ 10 | BmLitJsonWriter.exe \ 11 | BmLitJsonImport.exe \ 12 | BmLitJsonExport.exe \ 13 | BmLitJsonOutput.exe 14 | 15 | BENCHMARKS_NEWTONSOFT = \ 16 | BmNewtonsoftReader.exe \ 17 | BmNewtonsoftWriter.exe \ 18 | BmNewtonsoftImport.exe \ 19 | BmNewtonsoftExport.exe \ 20 | BmNewtonsoftOutput.exe 21 | 22 | BENCHMARKS = \ 23 | $(BENCHMARKS_JAYROCK) \ 24 | $(BENCHMARKS_LITJSON) \ 25 | $(BENCHMARKS_NEWTONSOFT) 26 | 27 | REF_JAYROCK = -r:Jayrock.Json.dll 28 | REF_LITJSON = -r:LitJson.dll 29 | REF_NEWTONSOFT = -r:Newtonsoft.Json.dll 30 | 31 | HEAPFILES = $(patsubst %.exe,%-heap.out,$(BENCHMARKS)) 32 | HEAP_SUMMARIES = $(patsubst %.exe,%-heap-summary.txt,$(BENCHMARKS)) 33 | 34 | HEAP_BUDDY = heap-buddy 35 | 36 | SOURCES_COMMON = \ 37 | Benchmark.cs \ 38 | BmCommon.cs 39 | 40 | 41 | 42 | CSC = gmcs 43 | CSC_FLAGS = 44 | RUNTIME = mono 45 | 46 | 47 | 48 | all: $(BENCHMARKS) 49 | @echo 50 | @echo "All benchmarks compiled, now you can run:" 51 | @echo " $(MAKE) benchmark-readers" 52 | @echo " $(MAKE) benchmark-writers" 53 | @echo " $(MAKE) benchmark-imports" 54 | @echo " $(MAKE) benchmark-exports" 55 | @echo 56 | @echo "And to run all benchmarks:" 57 | @echo " $(MAKE) benchmark" 58 | @echo 59 | @echo "To get information about heap usage (requires heap-buddy):" 60 | @echo " $(MAKE) benchmark-heap" 61 | 62 | clean: 63 | -rm -rf *.exe *.out *.txt 64 | 65 | distclean: 66 | -rm -rf *.exe *.out *.txt *.dll *.mdb 67 | 68 | benchmark: benchmark-readers benchmark-writers benchmark-imports benchmark-exports 69 | 70 | benchmark-readers: $(BENCHMARKS) 71 | @echo 72 | @echo "Running JSON readers benchmarks" 73 | @$(RUNTIME) BmJayrockReader.exe 74 | @$(RUNTIME) BmLitJsonReader.exe 75 | @$(RUNTIME) BmNewtonsoftReader.exe 76 | 77 | benchmark-writers: $(BENCHMARKS) 78 | @echo 79 | @echo "Running JSON writers benchmarks" 80 | @$(RUNTIME) BmJayrockWriter.exe 81 | @$(RUNTIME) BmLitJsonWriter.exe 82 | @$(RUNTIME) BmNewtonsoftWriter.exe 83 | 84 | benchmark-imports: $(BENCHMARKS) 85 | @echo 86 | @echo "Running JSON object imports benchmarks" 87 | @$(RUNTIME) BmJayrockImport.exe 88 | @$(RUNTIME) BmLitJsonImport.exe 89 | @$(RUNTIME) BmNewtonsoftImport.exe 90 | 91 | benchmark-exports: $(BENCHMARKS) 92 | @echo 93 | @echo "Running JSON object exports benchmarks" 94 | @$(RUNTIME) BmJayrockExport.exe 95 | @$(RUNTIME) BmLitJsonExport.exe 96 | @$(RUNTIME) BmNewtonsoftExport.exe 97 | 98 | benchmark-heap: $(HEAP_SUMMARIES) 99 | 100 | output: $(BENCHMARKS) 101 | @echo 102 | @echo "Producing output files" 103 | @$(RUNTIME) BmJayrockOutput.exe 104 | @$(RUNTIME) BmLitJsonOutput.exe 105 | @$(RUNTIME) BmNewtonsoftOutput.exe 106 | 107 | 108 | $(BENCHMARKS_JAYROCK): REF = $(REF_JAYROCK) 109 | $(BENCHMARKS_LITJSON): REF = $(REF_LITJSON) 110 | $(BENCHMARKS_NEWTONSOFT): REF = $(REF_NEWTONSOFT) 111 | 112 | $(BENCHMARKS): %.exe: %.cs $(SOURCES_COMMON) 113 | $(CSC) $(CSC_FLAGS) -out:$@ $(REF) $< $(SOURCES_COMMON) 114 | 115 | $(HEAPFILES): %-heap.out: %.exe 116 | $(RUNTIME) --profile=heap-buddy:$@ $< 117 | 118 | $(HEAP_SUMMARIES): %-heap-summary.txt: %-heap.out 119 | @echo "Creating summary for $*.exe" 120 | @$(HEAP_BUDDY) $< summary > $@ 121 | 122 | 123 | .PHONY: benchmark benchmark-readers benchmark-writers benchmark-imports benchmark-exports clean 124 | -------------------------------------------------------------------------------- /benchmarks/README: -------------------------------------------------------------------------------- 1 | LitJSON Benchmarks 2 | ================== 3 | 4 | This directory contains a number of benchmarks to verify the speed and heap 5 | usage of different pieces of LitJSON, and compare these items against two 6 | alternative JSON libraries: Jayrock[1] and Newtonsoft Json.NET[2]. 7 | 8 | Although raw speed is not the main priority of LitJSON, it's intended to 9 | have fairly good performance. The purpose of these benchmarks is mainly to 10 | keep track of this, and make sure it is not significantly worse than 11 | average. These benchmarks have certainly helped spot several problems with 12 | the original way things were being done, and much gratitude goes towards the 13 | creators of Jayrock and Newtonsoft Json.NET; without their excellent work, 14 | this library would not be what it is now. 15 | 16 | The benchmarks are split in several files, according to functionality and 17 | library being tested. With that in mind, source files follow the naming 18 | convention 'Bm[LIB][FUNC].cs' where 'LIB' is one of 'Jayrock', 'LitJson' or 19 | 'Newtonsoft', and [FUNC] is one of: 20 | 21 | * Reader: Test the Reader interface of the libraries 22 | * Writer: Test the Writer interface of the libraries 23 | * Import: Test the conversion from JSON to .Net objects 24 | * Export: Test the conversion from .Net objects to JSON 25 | * Output: Not really benchmarks, just simple methods that output some 26 | data to a .txt file just for debugging purposes 27 | 28 | The main benchmarking program is Benchmark.cs by Jon Skeet[3]. 29 | 30 | To compile and run the benchmarks you may use GNU make. A 'GNUmakefile' is 31 | provided, so all you need is to run 'make' (or 'gmake', if that's the name 32 | of the binary on your system) on this directory. 33 | 34 | Please notice that in order to compile the benchmarks you have to put the 35 | required .dll's on this directory as well. LitJson.dll can be copied from 36 | the bin/ directory once 'make' is run in the top directory. Jayrock's and 37 | Newtonsoft's dll files are not distributed with LitJSON, so you need to get 38 | them from their websites, and then put them here. 39 | 40 | If you want to get information regarding heap usage of the different 41 | benchmarks, you need to have heap-buddy[4] installed as well. Running 'make' 42 | without arguments should give you further instructions on how to proceed. 43 | 44 | --- 45 | [1] http://jayrock.berlios.de/ 46 | [2] http://www.newtonsoft.com/products/json/ 47 | [3] Benchmark.cs by Jon Skeet, copied verbatim from 48 | http://www.yoda.arachsys.com/csharp/benchmark.html 49 | [4] http://www.mono-project.com/HeapBuddy 50 | -------------------------------------------------------------------------------- /build/vs2012/LitJson.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9F728E2C-BFFA-4AA3-A906-3DF675A287FB} 8 | Library 9 | Properties 10 | LitJson.Tests 11 | LitJson.Tests 12 | $([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)..\..")) 13 | 512 14 | 17 | 3021 18 | v3.5 19 | 20 | 21 | 22 | AnyCPU 23 | true 24 | pdbonly 25 | false 26 | bin\ 27 | TRACE;DEBUG 28 | prompt 29 | 4 30 | true 31 | Off 32 | true 33 | 34 | 35 | AnyCPU 36 | none 37 | true 38 | bin\ 39 | TRACE 40 | prompt 41 | 4 42 | true 43 | Off 44 | 45 | 46 | Program 47 | $(NUnitBin)\nunit.exe 48 | /run "$(MSBuildProjectDirectory)\$(OutputPath)$(AssemblyName).dll" 49 | 50 | 51 | 52 | 53 | json-example.txt 54 | 55 | 56 | 57 | 58 | 59 | $(NUnitBin)\nunit.framework.dll 60 | 61 | 62 | {2b035ea1-5a3f-4b7c-8acb-2241314bf7d1} 63 | LitJson 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /build/vs2012/LitJson.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1} 8 | Library 9 | Properties 10 | LitJson 11 | LitJson 12 | $([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)..\..")) 13 | 512 14 | 17 | 3021 18 | v3.5 19 | 20 | 21 | 22 | AnyCPU 23 | true 24 | pdbonly 25 | false 26 | bin\ 27 | TRACE;DEBUG 28 | prompt 29 | 4 30 | true 31 | Off 32 | 33 | 34 | AnyCPU 35 | none 36 | true 37 | bin\ 38 | TRACE 39 | prompt 40 | 4 41 | true 42 | Off 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /build/vs2012/LitJson.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Express 2012 for Windows Desktop 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LitJson", "LitJson.csproj", "{2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LitJson.Tests", "LitJson.Tests.csproj", "{9F728E2C-BFFA-4AA3-A906-3DF675A287FB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2B035EA1-5A3F-4B7C-8ACB-2241314BF7D1}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {9F728E2C-BFFA-4AA3-A906-3DF675A287FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {9F728E2C-BFFA-4AA3-A906-3DF675A287FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {9F728E2C-BFFA-4AA3-A906-3DF675A287FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {9F728E2C-BFFA-4AA3-A906-3DF675A287FB}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /test/JsonAliasTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace LitJson.Test { 4 | 5 | class TestSoftAlias { 6 | [JsonAlias("f", true)] 7 | public int field; 8 | [JsonAlias("p", true)] 9 | public int property { get; set; } 10 | } 11 | 12 | class TestHardAlias { 13 | [JsonAlias("f")] 14 | public int field; 15 | [JsonAlias("p")] 16 | public int property { get; set; } 17 | } 18 | 19 | class TestDuplicateAlias1 { 20 | [JsonAlias("dup")] 21 | public int field1 = 0; 22 | [JsonAlias("dup")] 23 | public int field2 = 0; 24 | } 25 | 26 | class TestDuplicateAlias2 { 27 | [JsonAlias("dup")] 28 | public int field = 0; 29 | public int dup = 0; 30 | } 31 | 32 | class TestDuplicateAlias3 { 33 | [JsonAlias("dup")] 34 | public int dup = 0; 35 | } 36 | 37 | class TestDuplicateAlias4 { 38 | [JsonAlias("dup")] 39 | public int property1 { get; set; } 40 | [JsonAlias("dup")] 41 | public int property2 { get; set; } 42 | } 43 | 44 | class TestDuplicateAlias5 { 45 | [JsonAlias("dup")] 46 | public int property { get; set; } 47 | public int dup { get; set; } 48 | } 49 | 50 | [TestFixture] 51 | public sealed class JsonAliasTest { 52 | 53 | [Test] 54 | public void test_soft_alias_serializing() { 55 | var obj = new TestSoftAlias(); 56 | obj.field = 1; 57 | obj.property = 2; 58 | string json = JsonMapper.ToJson(obj); 59 | Assert.AreEqual("{\"p\":2,\"f\":1}", json, "A1"); 60 | } 61 | 62 | [Test] 63 | public void test_soft_alias_deserializing() { 64 | string json = "{\"p\":2,\"f\":1}"; 65 | var obj = JsonMapper.ToObject(json); 66 | Assert.IsNotNull(obj, "A1"); 67 | Assert.AreEqual(1, obj.field, "A2"); 68 | Assert.AreEqual(2, obj.property, "A3"); 69 | } 70 | 71 | [Test] 72 | public void test_soft_alias_deserializing_original_names() { 73 | string json = "{\"property\":2,\"field\":1}"; 74 | var obj = JsonMapper.ToObject(json); 75 | Assert.IsNotNull(obj, "A1"); 76 | Assert.AreEqual(1, obj.field, "A2"); 77 | Assert.AreEqual(2, obj.property, "A3"); 78 | } 79 | 80 | [Test] 81 | public void test_hard_alias_serializing() { 82 | var obj = new TestHardAlias(); 83 | obj.field = 1; 84 | obj.property = 2; 85 | string json = JsonMapper.ToJson(obj); 86 | Assert.AreEqual("{\"p\":2,\"f\":1}", json, "A1"); 87 | } 88 | 89 | [Test] 90 | public void test_hard_alias_deserializing() { 91 | string json = "{\"p\":2,\"f\":1}"; 92 | var obj = JsonMapper.ToObject(json); 93 | Assert.IsNotNull(obj, "A1"); 94 | Assert.AreEqual(1, obj.field, "A2"); 95 | Assert.AreEqual(2, obj.property, "A3"); 96 | } 97 | 98 | [Test] 99 | public void test_hard_alias_deserializing_original_names() { 100 | string json = "{\"property\":2,\"field\":1}"; 101 | var obj = JsonMapper.ToObject(json); 102 | Assert.AreEqual(default(int), obj.field, "A1"); 103 | Assert.AreEqual(default(int), obj.property, "A2"); 104 | } 105 | 106 | [Test, ExpectedException("LitJson.JsonException")] 107 | public void test_alias_duplicate_1() { 108 | JsonMapper.ToJson(new TestDuplicateAlias1()); 109 | } 110 | 111 | [Test, ExpectedException("LitJson.JsonException")] 112 | public void test_alias_duplicate_2() { 113 | JsonMapper.ToJson(new TestDuplicateAlias2()); 114 | } 115 | 116 | [Test, ExpectedException("LitJson.JsonException")] 117 | public void test_alias_duplicate_3() { 118 | JsonMapper.ToJson(new TestDuplicateAlias3()); 119 | } 120 | 121 | [Test, ExpectedException("LitJson.JsonException")] 122 | public void test_alias_duplicate_4() { 123 | JsonMapper.ToJson(new TestDuplicateAlias4()); 124 | } 125 | 126 | [Test, ExpectedException("LitJson.JsonException")] 127 | public void test_alias_duplicate_5() { 128 | JsonMapper.ToJson(new TestDuplicateAlias5()); 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /test/JsonArrayImporterTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace LitJson.Test { 4 | 5 | class ArrayImportRoot { 6 | public ArrayImport test = null; 7 | } 8 | 9 | class ArrayImport { 10 | public int x, y, z; 11 | } 12 | 13 | [TestFixture] 14 | public class JsonOArrayImporterTest { 15 | 16 | [Test] 17 | public void test_root_array_uses_importer() { 18 | bool usedImporter = false; 19 | JsonMapper.RegisterImporter(delegate(JsonData data) { 20 | ArrayImport obj = new ArrayImport(); 21 | obj.x = (int)data[0]; 22 | obj.y = (int)data[1]; 23 | obj.z = (int)data[2]; 24 | usedImporter = true; 25 | return obj; 26 | }); 27 | string json = @"[1, 2, 3]"; 28 | ArrayImport obj1 = JsonMapper.ToObject(json); 29 | Assert.IsNotNull(obj1); 30 | Assert.IsTrue(usedImporter); 31 | Assert.AreEqual(obj1.x, 1); 32 | Assert.AreEqual(obj1.y, 2); 33 | Assert.AreEqual(obj1.z, 3); 34 | } 35 | 36 | [Test] 37 | public void test_nonroot_array_uses_importer() { 38 | bool usedImporter = false; 39 | JsonMapper.RegisterImporter(delegate(JsonData data) { 40 | ArrayImport obj = new ArrayImport(); 41 | obj.x = (int)data[0]; 42 | obj.y = (int)data[1]; 43 | obj.z = (int)data[2]; 44 | usedImporter = true; 45 | return obj; 46 | }); 47 | string json = @"{""test"":[1, 2, 3]}"; 48 | ArrayImportRoot root = JsonMapper.ToObject(json); 49 | Assert.IsNotNull(root); 50 | Assert.IsNotNull(root.test); 51 | Assert.IsTrue(usedImporter); 52 | Assert.AreEqual(root.test.x, 1); 53 | Assert.AreEqual(root.test.y, 2); 54 | Assert.AreEqual(root.test.z, 3); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /test/JsonDataTest.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonDataTest.cs 4 | * Tests for the JsonData class. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using LitJson; 13 | using NUnit.Framework; 14 | using System; 15 | using System.Collections.Generic; 16 | 17 | namespace LitJson.Test { 18 | 19 | [TestFixture] 20 | public class JsonDataTest { 21 | 22 | [Test] 23 | public void AsArrayTest() { 24 | JsonData data = new JsonData(); 25 | data.Add(1); 26 | data.Add(2); 27 | data.Add(3); 28 | data.Add("Launch!"); 29 | Assert.IsTrue(data.IsArray, "A1"); 30 | Assert.AreEqual("[1,2,3,\"Launch!\"]", data.ToJson(), "A2"); 31 | } 32 | 33 | [Test] 34 | public void AsBooleanTest() { 35 | JsonData data; 36 | data = true; 37 | Assert.IsTrue(data.IsBoolean, "A1"); 38 | Assert.IsTrue((bool)data, "A2"); 39 | Assert.AreEqual("true", data.ToJson(), "A3"); 40 | data = false; 41 | bool f = false; 42 | Assert.AreEqual(f, (bool)data, "A4"); 43 | } 44 | 45 | [Test] 46 | public void AsDoubleTest() { 47 | JsonData data; 48 | 49 | data = 3e6; 50 | Assert.IsTrue(data.IsReal, "A1"); 51 | Assert.AreEqual(3e6, (double)data, "A2"); 52 | Assert.AreEqual("3000000.0", data.ToJson(), "A3"); 53 | 54 | data = 3.14; 55 | Assert.IsTrue(data.IsReal, "A4"); 56 | Assert.AreEqual(3.14, (double)data, "A5"); 57 | Assert.AreEqual("3.14", data.ToJson(), "A6"); 58 | 59 | data = 0.123; 60 | double n = 0.123; 61 | 62 | Assert.AreEqual(n, (double)data, "A7"); 63 | } 64 | 65 | [Test] 66 | public void AsIntTest() { 67 | JsonData data; 68 | 69 | data = 13; 70 | Assert.IsTrue(data.IsNatural, "A1"); 71 | Assert.AreEqual((int)data, 13, "A2"); 72 | Assert.AreEqual(data.ToJson(), "13", "A3"); 73 | 74 | data = -00500; 75 | 76 | Assert.IsTrue(data.IsNatural, "A4"); 77 | Assert.AreEqual((int) data, -500, "A5"); 78 | Assert.AreEqual(data.ToJson(), "-500", "A6"); 79 | 80 | data = 1024; 81 | int n = 1024; 82 | 83 | Assert.AreEqual((int) data, n, "A7"); 84 | } 85 | 86 | [Test] 87 | public void AsObjectTest() { 88 | JsonData data = new JsonData(); 89 | 90 | data["alignment"] = "left"; 91 | data["font"] = new JsonData(); 92 | data["font"]["name"] = "Arial"; 93 | data["font"]["style"] = "italic"; 94 | data["font"]["size"] = 10; 95 | data["font"]["color"] = "#fff"; 96 | 97 | Assert.IsTrue(data.IsObject, "A1"); 98 | 99 | string json = "{\"alignment\":\"left\",\"font\":{" + 100 | "\"name\":\"Arial\",\"style\":\"italic\",\"size\":10," + 101 | "\"color\":\"#fff\"}}"; 102 | 103 | Assert.AreEqual(json, data.ToJson(), "A2"); 104 | } 105 | 106 | [Test] 107 | public void AsStringTest() { 108 | JsonData data; 109 | 110 | data = "All you need is love"; 111 | Assert.IsTrue(data.IsString, "A1"); 112 | Assert.AreEqual("All you need is love", (string) data, "A2"); 113 | Assert.AreEqual("\"All you need is love\"", data.ToJson(), 114 | "A3"); 115 | } 116 | 117 | [Test] 118 | public void EqualsTest() { 119 | JsonData a; 120 | JsonData b; 121 | 122 | // Compare ints 123 | a = 7; 124 | b = 7; 125 | Assert.IsTrue(a.Equals(b), "A1"); 126 | 127 | Assert.IsFalse(a.Equals(null), "A2"); 128 | 129 | b = 8; 130 | Assert.IsFalse(a.Equals(b), "A3"); 131 | 132 | // Compare longs 133 | a = 10L; 134 | b = 10L; 135 | Assert.IsTrue(a.Equals(b), "A4"); 136 | 137 | b = 10; 138 | Assert.IsTrue(a.Equals(b), "A5"); 139 | b = 11L; 140 | Assert.IsFalse(a.Equals(b), "A6"); 141 | 142 | // Compare doubles 143 | a = 78.9; 144 | b = 78.9; 145 | Assert.IsTrue(a.Equals(b), "A7"); 146 | 147 | b = 78.899999; 148 | Assert.IsFalse(a.Equals(b), "A8"); 149 | 150 | // Compare booleans 151 | a = true; 152 | b = true; 153 | Assert.IsTrue(a.Equals(b), "A9"); 154 | 155 | b = false; 156 | Assert.IsFalse(a.Equals(b), "A10"); 157 | 158 | // Compare strings 159 | a = "walrus"; 160 | b = "walrus"; 161 | Assert.IsTrue(a.Equals(b), "A11"); 162 | 163 | b = "Walrus"; 164 | Assert.IsFalse(a.Equals(b), "A12"); 165 | } 166 | 167 | [Test] 168 | public void GetKeysTest() { 169 | JsonData data = new JsonData(); 170 | 171 | data["first"] = "one"; 172 | data["second"] = "two"; 173 | data["third"] = "three"; 174 | data["fourth"] = "four"; 175 | 176 | Assert.AreEqual(4, data.Keys.Count, "A1"); 177 | 178 | foreach (string k in data.Keys) { 179 | Assert.IsNotNull(data[k], "A2"); 180 | } 181 | } 182 | 183 | [Test] 184 | [ExpectedException (typeof(InvalidOperationException))] 185 | public void GetKeysInvalidTypeTest() { 186 | JsonData data = new JsonData(); 187 | data.Add (42); // turns it into an array 188 | 189 | // .. but an array doesn't have keys 190 | ICollection keys = data.Keys; 191 | Assert.IsNotNull (keys); 192 | } 193 | 194 | [Test] 195 | [ExpectedException (typeof(InvalidCastException))] 196 | public void InvalidCastTest() { 197 | JsonData data = "test"; 198 | bool boolean = (bool)data; 199 | if (boolean != (bool)data) { 200 | boolean = (bool)data; 201 | } 202 | } 203 | 204 | [Test] 205 | public void NullValue() { 206 | string json = "{\"test\":null}"; 207 | 208 | JsonData data = new JsonData(); 209 | data["test"] = null; 210 | 211 | Assert.AreEqual(json, data.ToJson()); 212 | } 213 | 214 | [Test] 215 | public void PropertiesOrderTest() { 216 | JsonData data = new JsonData(); 217 | string json = "{\"first\":\"one\",\"second\":\"two\",\"third\":\"three\",\"fourth\":\"four\"}"; 218 | for (int i = 0; i < 10; i++) { 219 | data.Clear (); 220 | data["first"] = "one"; 221 | data["second"] = "two"; 222 | data["third"] = "three"; 223 | data["fourth"] = "four"; 224 | Assert.AreEqual(json, data.ToJson()); 225 | } 226 | } 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /test/JsonIgnoreMemberTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace LitJson.Test { 4 | 5 | [JsonIgnoreMember("ignoreMe")] 6 | class TestIgnoreFieldMember { 7 | public string ignoreMe; 8 | public string dontIgnoreMe; 9 | } 10 | 11 | class TestIgnoreFieldMemberDerived : TestIgnoreFieldMember { 12 | public string dontIgnoreMeDerived; 13 | } 14 | 15 | [JsonIgnoreMember("ignoreMe")] 16 | class TestIgnorePropMember { 17 | public string ignoreMe { get; set; } 18 | public string dontIgnoreMe { get; set; } 19 | } 20 | 21 | class TestIgnorePropMemberDerived : TestIgnorePropMember { 22 | public string dontIgnoreMeDerived { get; set; } 23 | } 24 | 25 | class TestIgnoreBareFieldMember { 26 | public string ignoreMe; 27 | } 28 | 29 | [JsonIgnoreMember("ignoreMe")] 30 | class TestIgnoreBareFieldMemberDerived : TestIgnoreBareFieldMember { 31 | public string dontIgnoreMe; 32 | } 33 | 34 | class TestIgnoreBareFieldMemberDerivedDerived : TestIgnoreBareFieldMemberDerived { 35 | public string dontIgnoreMeDerived; 36 | } 37 | 38 | [TestFixture] 39 | public class JsonIgnoreMemberTest { 40 | 41 | [Test] 42 | public void test_noninherited_field_member_is_ignored() { 43 | var input = new TestIgnoreFieldMember(); 44 | input.ignoreMe = "ignored"; 45 | input.dontIgnoreMe = "not_ignored"; 46 | string json = JsonMapper.ToJson(input); 47 | Assert.IsNotNull(json); 48 | var output = JsonMapper.ToObject(json); 49 | Assert.IsNotNull(output); 50 | Assert.IsNull(output.ignoreMe); 51 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 52 | } 53 | 54 | [Test] 55 | public void test_inherited_field_member_is_ignored() { 56 | var input = new TestIgnoreFieldMemberDerived(); 57 | input.ignoreMe = "ignored"; 58 | input.dontIgnoreMe = "not_ignored"; 59 | input.dontIgnoreMeDerived = "not_ignored_derived"; 60 | string json = JsonMapper.ToJson(input); 61 | Assert.IsNotNull(json); 62 | var output = JsonMapper.ToObject(json); 63 | Assert.IsNotNull(output); 64 | Assert.IsNull(output.ignoreMe); 65 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 66 | Assert.AreEqual("not_ignored_derived", output.dontIgnoreMeDerived); 67 | } 68 | 69 | [Test] 70 | public void test_noninherited_property_member_is_ignored() { 71 | var input = new TestIgnorePropMember(); 72 | input.ignoreMe = "ignored"; 73 | input.dontIgnoreMe = "not_ignored"; 74 | string json = JsonMapper.ToJson(input); 75 | Assert.IsNotNull(json); 76 | var output = JsonMapper.ToObject(json); 77 | Assert.IsNotNull(output); 78 | Assert.IsNull(output.ignoreMe); 79 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 80 | } 81 | 82 | [Test] 83 | public void test_inherited_property_member_is_ignored() { 84 | var input = new TestIgnorePropMemberDerived(); 85 | input.ignoreMe = "ignored"; 86 | input.dontIgnoreMe = "not_ignored"; 87 | input.dontIgnoreMeDerived = "not_ignored_derived"; 88 | string json = JsonMapper.ToJson(input); 89 | Assert.IsNotNull(json); 90 | var output = JsonMapper.ToObject(json); 91 | Assert.IsNotNull(output); 92 | Assert.IsNull(output.ignoreMe); 93 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 94 | Assert.AreEqual("not_ignored_derived", output.dontIgnoreMeDerived); 95 | } 96 | 97 | [Test] 98 | public void test_bare_inherited_field_member_is_ignored() { 99 | var input = new TestIgnoreBareFieldMemberDerived(); 100 | input.ignoreMe = "ignored"; 101 | input.dontIgnoreMe = "not_ignored"; 102 | string json = JsonMapper.ToJson(input); 103 | Assert.IsNotNull(json); 104 | var output = JsonMapper.ToObject(json); 105 | Assert.IsNotNull(output); 106 | Assert.IsNull(output.ignoreMe); 107 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 108 | } 109 | 110 | [Test] 111 | public void test_bare_derived_inherited_field_member_is_ignored() { 112 | var input = new TestIgnoreBareFieldMemberDerivedDerived(); 113 | input.ignoreMe = "ignored"; 114 | input.dontIgnoreMe = "not_ignored"; 115 | input.dontIgnoreMeDerived = "not_ignored_derived"; 116 | string json = JsonMapper.ToJson(input); 117 | Assert.IsNotNull(json); 118 | var output = JsonMapper.ToObject(json); 119 | Assert.IsNotNull(output); 120 | Assert.IsNull(output.ignoreMe); 121 | Assert.AreEqual("not_ignored", output.dontIgnoreMe); 122 | Assert.AreEqual("not_ignored_derived", output.dontIgnoreMeDerived); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /test/JsonObjectImporterTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace LitJson.Test 4 | { 5 | class ObjectImportRoot 6 | { 7 | public ObjectImport test = null; 8 | } 9 | 10 | class ObjectImport 11 | { 12 | public int value; 13 | } 14 | 15 | class NestedObjectImport 16 | { 17 | public ObjectImport test = null; 18 | } 19 | 20 | [TestFixture] 21 | public class JsonObjectImporterTest 22 | { 23 | [Test] 24 | public void test_root_object_uses_importer() 25 | { 26 | bool usedImporter = false; 27 | JsonMapper.RegisterImporter(delegate(JsonData data) { 28 | ObjectImport obj = new ObjectImport(); 29 | obj.value = (int)data["value"]; 30 | usedImporter = true; 31 | return obj; 32 | }); 33 | string json = @"{""value"":11}"; 34 | ObjectImport obj1 = JsonMapper.ToObject(json); 35 | Assert.IsNotNull(obj1); 36 | Assert.IsTrue(usedImporter); 37 | Assert.AreEqual(obj1.value, 11); 38 | } 39 | 40 | [Test] 41 | public void test_nonroot_object_uses_importer() 42 | { 43 | bool usedImporter = false; 44 | JsonMapper.RegisterImporter(delegate(JsonData data) { 45 | ObjectImport obj = new ObjectImport(); 46 | obj.value = (int)data["value"]; 47 | usedImporter = true; 48 | return obj; 49 | }); 50 | string json = @"{""test"":{""value"":11},""testStruct"":{""x"":1,""y"":2,""z"":3}}"; 51 | ObjectImportRoot root = JsonMapper.ToObject(json); 52 | Assert.IsNotNull(root); 53 | Assert.IsNotNull(root.test); 54 | Assert.IsTrue(usedImporter); 55 | Assert.AreEqual(root.test.value, 11); 56 | } 57 | 58 | // [Test] 59 | // public void test_nested_object_both_use_custom_importers() 60 | // { 61 | // bool usedImporter = false; 62 | // bool usedNestedImporter = false; 63 | // JsonMapper.RegisterImporter(delegate(JsonData data) { 64 | // ObjectImport obj = new ObjectImport(); 65 | // obj.value = (int)data["value"]; 66 | // usedImporter = true; 67 | // return obj; 68 | // }); 69 | // JsonMapper.RegisterImporter(delegate(JsonData data) { 70 | // NestedObjectImport obj = new NestedObjectImport(); 71 | // System.Console.WriteLine(data["test"]); 72 | // obj.test = (ObjectImport)(object)data["test"]; 73 | // usedNestedImporter = true; 74 | // return obj; 75 | // }); 76 | // string json = @"{""test"":{""value"":11}}"; 77 | // NestedObjectImport root = JsonMapper.ToObject(json); 78 | // Assert.IsNotNull(root); 79 | // Assert.IsNotNull(root.test); 80 | // Assert.IsTrue(usedImporter); 81 | // Assert.IsTrue(usedNestedImporter); 82 | // Assert.AreEqual(root.test.value, 11); 83 | // } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/JsonReaderTest.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonReaderTest.cs 4 | * Tests for the JsonReader class. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using LitJson; 13 | using NUnit.Framework; 14 | using System; 15 | using System.IO; 16 | using System.Reflection; 17 | 18 | 19 | namespace LitJson.Test 20 | { 21 | [TestFixture] 22 | public class JsonReaderTest 23 | { 24 | [Test] 25 | public void BooleanTest () 26 | { 27 | string json = "[ true, false ]"; 28 | 29 | JsonReader reader = new JsonReader (json); 30 | reader.Read(); 31 | 32 | reader.Read(); 33 | Assert.IsTrue((bool) reader.Value, "A1"); 34 | reader.Read(); 35 | Assert.IsTrue(! ((bool) reader.Value), "A2"); 36 | 37 | reader.Close (); 38 | } 39 | 40 | [Test] 41 | public void CommentsTest() 42 | { 43 | string json = @" 44 | { 45 | // This is the first property 46 | ""foo"" : ""bar"", 47 | 48 | /** 49 | * This is the second property 50 | **/ 51 | ""baz"": ""blah"" 52 | }"; 53 | 54 | JsonReader reader = new JsonReader (json); 55 | 56 | reader.Read(); 57 | reader.Read(); 58 | Assert.AreEqual("foo", (string) reader.Value, "A1"); 59 | 60 | reader.Read(); 61 | reader.Read(); 62 | Assert.AreEqual("baz", (string) reader.Value, "A2"); 63 | 64 | reader.Read(); 65 | reader.Read(); 66 | Assert.IsTrue(reader.EndOfJson, "A3"); 67 | } 68 | 69 | [Test] 70 | public void DoubleTest () 71 | { 72 | string json = @"[ 0.0, -0.0, 3.1416, 8e-3, 7E-5, -128.000009, 73 | 144e+3, 0.1e2 ]"; 74 | 75 | JsonReader reader = new JsonReader (json); 76 | reader.Read(); 77 | 78 | reader.Read(); 79 | Assert.AreEqual((double) reader.Value, 0.0, 80 | Double.Epsilon, "A1"); 81 | reader.Read(); 82 | Assert.AreEqual((double) reader.Value, 0.0, 83 | Double.Epsilon, "A2"); 84 | reader.Read(); 85 | Assert.AreEqual((double) reader.Value, 3.1416, 86 | Double.Epsilon, "A3"); 87 | reader.Read(); 88 | Assert.AreEqual((double) reader.Value, 0.008, 89 | Double.Epsilon, "A4"); 90 | reader.Read(); 91 | Assert.AreEqual((double) reader.Value, 0.00007, 92 | Double.Epsilon, "A5"); 93 | reader.Read(); 94 | Assert.AreEqual((double) reader.Value, -128.000009, 95 | Double.Epsilon, "A6"); 96 | reader.Read(); 97 | Assert.AreEqual((double) reader.Value, 144000.0, 98 | Double.Epsilon, "A7"); 99 | reader.Read(); 100 | Assert.AreEqual((double) reader.Value, 10.0, 101 | Double.Epsilon, "A8"); 102 | 103 | reader.Close (); 104 | } 105 | 106 | [Test] 107 | public void EmptyStringTest () 108 | { 109 | string json = "[ \"\" ]"; 110 | 111 | JsonReader reader = new JsonReader (json); 112 | reader.Read(); 113 | 114 | reader.Read(); 115 | Assert.AreEqual(reader.Value, String.Empty); 116 | 117 | reader.Close (); 118 | } 119 | 120 | [Test] 121 | public void EndOfJsonTest () 122 | { 123 | string json = " [ 1 ] [ 2, 3 ] [ 4, 5, 6 ] "; 124 | 125 | JsonReader reader = new JsonReader (json); 126 | 127 | int i; 128 | for (i = 0; i < 3; i++) { 129 | Assert.IsFalse(reader.EndOfJson, "A1"); 130 | reader.Read(); 131 | } 132 | 133 | Assert.IsTrue(reader.EndOfJson, "A2"); 134 | Assert.IsFalse(reader.EndOfInput, "A3"); 135 | 136 | reader.Read(); 137 | 138 | for (i = 0; i < 3; i++) { 139 | Assert.IsFalse(reader.EndOfJson, "A4"); 140 | reader.Read(); 141 | } 142 | 143 | Assert.IsTrue(reader.EndOfJson, "A5"); 144 | Assert.IsFalse(reader.EndOfInput, "A6"); 145 | 146 | reader.Read(); 147 | 148 | for (i = 0; i < 4; i++) { 149 | Assert.IsFalse(reader.EndOfJson, "A7"); 150 | reader.Read(); 151 | } 152 | 153 | Assert.IsTrue(reader.EndOfJson, "A8"); 154 | 155 | reader.Read(); 156 | Assert.IsTrue(reader.EndOfInput, "A9"); 157 | } 158 | 159 | [Test] 160 | public void FromFileTest () 161 | { 162 | Assembly asmb = typeof(JsonReaderTest).Assembly; 163 | 164 | var fileStream = asmb.GetManifestResourceStream ("json-example.txt"); 165 | if (fileStream == null) 166 | { 167 | fileStream = asmb.GetManifestResourceStream ("litjsontest.json-example.txt"); 168 | } 169 | 170 | StreamReader stream = new StreamReader (fileStream); 171 | 172 | JsonReader reader = new JsonReader (stream); 173 | 174 | while (reader.Read()); 175 | } 176 | 177 | [Test] 178 | public void IntTest () 179 | { 180 | string json = "[ 0, -0, 123, 14400, -500 ]"; 181 | 182 | JsonReader reader = new JsonReader (json); 183 | reader.Read(); 184 | 185 | reader.Read(); 186 | Assert.AreEqual((long) reader.Value, 0, "A1"); 187 | reader.Read(); 188 | Assert.AreEqual((long) reader.Value, 0, "A2"); 189 | reader.Read(); 190 | Assert.AreEqual((long) reader.Value, 123, "A3"); 191 | reader.Read(); 192 | Assert.AreEqual((long) reader.Value, 14400, "A4"); 193 | reader.Read(); 194 | Assert.AreEqual((long) reader.Value, -500, "A5"); 195 | 196 | reader.Close (); 197 | } 198 | 199 | [Test] 200 | [ExpectedException (typeof(JsonException))] 201 | public void LexerErrorEscapeSequenceTest () 202 | { 203 | string json = "[ \"Hello World \\ufffg \" ]"; 204 | 205 | JsonReader reader = new JsonReader (json); 206 | 207 | while (reader.Read()); 208 | } 209 | 210 | [Test] 211 | [ExpectedException (typeof(JsonException))] 212 | public void LexerErrorRealNumberTest () 213 | { 214 | // One ore more digits have to appear after the '.' 215 | string json = "[ 0.e5 ]"; 216 | 217 | JsonReader reader = new JsonReader (json); 218 | 219 | while (reader.Read()); 220 | } 221 | 222 | [Test] 223 | [ExpectedException (typeof(JsonException))] 224 | public void LexerErrorTrueTest () 225 | { 226 | string json = "[ TRUE ]"; 227 | 228 | JsonReader reader = new JsonReader (json); 229 | 230 | while (reader.Read()); 231 | } 232 | 233 | [Test] 234 | [Category ("RuntimeBug")] // Int32.TryParse in mono 1.2.5 235 | public void LongTest () 236 | { 237 | string json = "[ 2147483648, -10000000000 ]"; 238 | 239 | JsonReader reader = new JsonReader (json); 240 | reader.Read(); 241 | 242 | reader.Read(); 243 | Assert.AreEqual(typeof(Int64), reader.Value.GetType (), "A1"); 244 | Assert.AreEqual(2147483648L, (long) reader.Value, "A2"); 245 | reader.Read(); 246 | Assert.AreEqual(-10000000000L, (long) reader.Value, "A3"); 247 | 248 | reader.Close (); 249 | } 250 | 251 | [Test] 252 | public void NestedArrays () 253 | { 254 | string json = "[ [ [ [ [ 1, 2, 3 ] ] ] ] ]"; 255 | 256 | int array_count = 0; 257 | 258 | JsonReader reader = new JsonReader (json); 259 | 260 | while (reader.Read()) { 261 | if (reader.Token == JsonToken.ArrayStart) 262 | array_count++; 263 | } 264 | 265 | Assert.AreEqual(array_count, 5); 266 | } 267 | 268 | [Test] 269 | public void NestedObjects () 270 | { 271 | string json = "{ \"obj1\": { \"obj2\": { \"obj3\": true } } }"; 272 | 273 | int object_count = 0; 274 | JsonReader reader = new JsonReader (json); 275 | 276 | while (reader.Read()) { 277 | if (reader.Token == JsonToken.ObjectStart) 278 | object_count++; 279 | } 280 | 281 | Assert.AreEqual(object_count, 3); 282 | } 283 | 284 | [Test] 285 | [ExpectedException (typeof(ArgumentNullException))] 286 | public void NullReaderTest () 287 | { 288 | TextReader text_reader = null; 289 | JsonReader reader = new JsonReader (text_reader); 290 | 291 | while (reader.Read()); 292 | } 293 | 294 | [Test] 295 | public void NullTest () 296 | { 297 | string json = "[ null ]"; 298 | 299 | JsonReader reader = new JsonReader (json); 300 | reader.Read(); 301 | 302 | reader.Read(); 303 | Assert.IsNull (reader.Value); 304 | 305 | reader.Close (); 306 | } 307 | 308 | [Test] 309 | [ExpectedException (typeof(JsonException))] 310 | public void ParserErrorArrayClosingTest () 311 | { 312 | string json = "[ 1, 2, 3 }"; 313 | 314 | JsonReader reader = new JsonReader (json); 315 | 316 | while (reader.Read()); 317 | } 318 | 319 | [Test] 320 | [ExpectedException (typeof(JsonException))] 321 | public void ParserErrorIncompleteObjectTest () 322 | { 323 | string json = "{ \"temperature\" : 21 "; 324 | 325 | JsonReader reader = new JsonReader (json); 326 | 327 | while (reader.Read()); 328 | } 329 | 330 | [Test] 331 | [ExpectedException (typeof(JsonException))] 332 | public void ParserErrorNoArrayOrObjectTest () 333 | { 334 | string json = "true"; 335 | 336 | JsonReader reader = new JsonReader (json); 337 | 338 | while (reader.Read()); 339 | } 340 | 341 | [Test] 342 | [ExpectedException (typeof(JsonException))] 343 | public void ParserErrorObjectClosingTest () 344 | { 345 | string json = @"{ 346 | ""sports"": [ 347 | ""football"", ""baseball"", ""basketball"" ] ]"; 348 | 349 | JsonReader reader = new JsonReader (json); 350 | 351 | while (reader.Read()); 352 | } 353 | 354 | [Test] 355 | [ExpectedException (typeof(JsonException))] 356 | public void ParserErrorPropertyExpectedTest () 357 | { 358 | string json = "{ {\"foo\": bar} }"; 359 | 360 | JsonReader reader = new JsonReader (json); 361 | 362 | while (reader.Read()); 363 | } 364 | 365 | [Test] 366 | public void QuickArrayTest () 367 | { 368 | string json = "[ \"George\", \"John\", \"Ringo\", \"Paul\" ]"; 369 | 370 | JsonReader reader = new JsonReader (json); 371 | 372 | reader.Read(); 373 | Assert.AreEqual(reader.Token, JsonToken.ArrayStart, "A1"); 374 | reader.Read(); 375 | Assert.AreEqual(reader.Token, JsonToken.String, "A2"); 376 | Assert.AreEqual(reader.Value, "George", "A3"); 377 | reader.Read(); 378 | Assert.AreEqual(reader.Token, JsonToken.String, "A3"); 379 | Assert.AreEqual(reader.Value, "John", "A4"); 380 | reader.Read(); 381 | Assert.AreEqual(reader.Token, JsonToken.String, "A6"); 382 | Assert.AreEqual(reader.Value, "Ringo", "A7"); 383 | reader.Read(); 384 | Assert.AreEqual(reader.Token, JsonToken.String, "A8"); 385 | Assert.AreEqual(reader.Value, "Paul", "A9"); 386 | reader.Read(); 387 | Assert.AreEqual(reader.Token, JsonToken.ArrayEnd, "A10"); 388 | reader.Read(); 389 | Assert.IsTrue(reader.EndOfJson, "A11"); 390 | } 391 | 392 | [Test] 393 | public void QuickObjectTest () 394 | { 395 | string json = @"{ 396 | ""vehicle"": ""submarine"", 397 | ""color"": ""yellow"" 398 | }"; 399 | 400 | JsonReader reader = new JsonReader (json); 401 | 402 | reader.Read(); 403 | Assert.AreEqual(reader.Token, JsonToken.ObjectStart, "A1"); 404 | reader.Read(); 405 | Assert.AreEqual(reader.Token, JsonToken.PropertyName, "A2"); 406 | Assert.AreEqual(reader.Value, "vehicle", "A3"); 407 | reader.Read(); 408 | Assert.AreEqual(reader.Token, JsonToken.String, "A4"); 409 | Assert.AreEqual(reader.Value, "submarine", "A5"); 410 | reader.Read(); 411 | Assert.AreEqual(reader.Token, JsonToken.PropertyName, "A6"); 412 | Assert.AreEqual(reader.Value, "color", "A7"); 413 | reader.Read(); 414 | Assert.AreEqual(reader.Token, JsonToken.String, "A8"); 415 | Assert.AreEqual(reader.Value, "yellow", "A9"); 416 | reader.Read(); 417 | Assert.AreEqual(reader.Token, JsonToken.ObjectEnd, "A10"); 418 | reader.Read(); 419 | Assert.IsTrue(reader.EndOfJson, "A11"); 420 | } 421 | 422 | [Test] 423 | [ExpectedException (typeof(JsonException))] 424 | public void StrictCommentsTest () 425 | { 426 | string json = @" 427 | [ 428 | // This is a comment 429 | 1, 430 | 2, 431 | 3 432 | ]"; 433 | 434 | JsonReader reader = new JsonReader (json); 435 | reader.AllowComments = false; 436 | 437 | while (reader.Read()); 438 | } 439 | 440 | [Test] 441 | [ExpectedException (typeof(JsonException))] 442 | public void StrictStringsTest () 443 | { 444 | string json = "[ 'Look! Single quotes' ]"; 445 | 446 | JsonReader reader = new JsonReader (json); 447 | reader.AllowSingleQuotedStrings = false; 448 | 449 | while (reader.Read()); 450 | } 451 | 452 | [Test] 453 | public void StringsTest () 454 | { 455 | string json = 456 | "[ \"abc 123 \\n\\f\\b\\t\\r \\\" \\\\ \\u263a \\u25CF\" ]"; 457 | 458 | string str = "abc 123 \n\f\b\t\r \" \\ \u263a \u25cf"; 459 | 460 | JsonReader reader = new JsonReader (json); 461 | reader.Read(); 462 | reader.Read(); 463 | 464 | Assert.AreEqual(str, reader.Value, "A1"); 465 | 466 | reader.Close (); 467 | 468 | json = " [ '\"Hello\" \\'world\\'' ] "; 469 | str = "\"Hello\" 'world'"; 470 | 471 | reader = new JsonReader (json); 472 | reader.Read(); 473 | reader.Read(); 474 | 475 | Assert.AreEqual(str, reader.Value, "A2"); 476 | 477 | reader.Close (); 478 | } 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /test/JsonRoundTripTest.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonRoundTripTest.cs 4 | * Tests using the JsonMapper to do round-trip testing, convert to json and back again, check equal. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using LitJson; 13 | using NUnit.Framework; 14 | using System; 15 | using System.Collections; 16 | using System.Collections.Generic; 17 | using System.Collections.Specialized; 18 | using System.IO; 19 | using System.Reflection; 20 | 21 | // Q: should we handle non-numeric values correctly, like Infinity and NaN? (A: yes) 22 | 23 | namespace LitJson.Test 24 | { 25 | [TestFixture] 26 | public class RoundTripTest 27 | { 28 | [Test] 29 | public void RoundTripInt8() 30 | { 31 | RoundTrip(0, 1, Byte.MaxValue, Byte.MinValue); 32 | } 33 | 34 | [Test] 35 | public void RoundTripInt16() 36 | { 37 | RoundTrip(0, 1, Int16.MaxValue, Int16.MinValue); 38 | } 39 | 40 | [Test] 41 | public void RoundTripInt32() 42 | { 43 | RoundTrip(0, 1, Int16.MaxValue, Int16.MinValue, Int32.MaxValue, Int32.MinValue); 44 | } 45 | 46 | 47 | [Test] 48 | public void RoundTripInt64() 49 | { 50 | RoundTrip(0, 1, Int16.MaxValue, Int16.MinValue, Int32.MaxValue, Int32.MinValue, Int64.MaxValue, Int64.MinValue); 51 | } 52 | 53 | [Test] 54 | public void RoundTripUInt16() 55 | { 56 | RoundTrip(0, 1, UInt16.MaxValue, UInt16.MinValue); 57 | } 58 | 59 | [Test] 60 | public void RoundTripUInt32() 61 | { 62 | RoundTrip(0, 1, UInt16.MaxValue, UInt16.MinValue, UInt32.MaxValue, UInt32.MinValue); 63 | } 64 | 65 | 66 | [Test] 67 | public void RoundTripUInt64() 68 | { 69 | RoundTrip(0, 1, UInt16.MaxValue, UInt16.MinValue, UInt32.MaxValue, UInt32.MinValue, UInt64.MaxValue, UInt64.MinValue); 70 | } 71 | 72 | [Test] 73 | public void RoundTripFloatSingle() 74 | { 75 | RoundTrip(CompareSingle, true, 0.0f, 1.0f, 0.1f, 0.123456789f, 123456789.123456789f, Single.Epsilon, Single.MinValue, Single.MaxValue); 76 | } 77 | 78 | [Test] 79 | public void RoundTripFloatDouble() 80 | { 81 | RoundTrip(CompareDouble, true, 0.0, 1.0, 0.1, 0.123456789, 123456789.123456789, Single.Epsilon, Single.MinValue, Single.MaxValue, Double.Epsilon, Double.MinValue, Double.MaxValue); 82 | } 83 | 84 | [Test] 85 | public void RoundTripFloatDecimal() 86 | { 87 | RoundTrip(CompareDecimal, true, 0.0M, 1.0M, 0.1M, 0.123456789M, 123456789.123456789M, Decimal.MinValue, Decimal.MaxValue); 88 | } 89 | 90 | [Test] 91 | public void RoundTripFloatSingleRandom() 92 | { 93 | RoundTripFloatRandom(CompareSingle, Single.MinValue, Single.MaxValue); 94 | } 95 | 96 | [Test] 97 | public void RoundTripFloatDoubleRandom() 98 | { 99 | RoundTripFloatRandom(CompareDouble, Double.MinValue * 0.9, Double.MaxValue * 0.9); 100 | } 101 | 102 | [Test] 103 | public void RoundTripFloatDecimalRandom() 104 | { 105 | RoundTripFloatRandom(CompareDecimal, (double)Decimal.MinValue, (double)Decimal.MaxValue); 106 | } 107 | 108 | // NOTE: the comparison functions wouldn't be needed if json round-tripped correctly for float and decimal numbers. 109 | // It does for double, because the reader always uses double. To make it work for the others we should defer string 110 | // parsing until the target numeric type is known -- is this possible within the existing framework? 111 | private int CompareSingle(float x, float y) 112 | { 113 | #if true 114 | // TODO: this is what we should be doing, however this doesn't work at present, because LitJson parses float strings as doubles, 115 | // which can lose precision in the lower order bit. 116 | // Hmm, interesting, seems to be working fine?? 117 | return Comparer.Default.Compare(x, y); 118 | #else 119 | if (x == y) 120 | return 0; 121 | 122 | // Jump through unsafe hoops to compare via binary representation of floating point numbers. 123 | unsafe 124 | { 125 | int xi = *(int*)&x; 126 | int yi = *(int*)&y; 127 | int diff = xi - yi; 128 | 129 | // +/-1 are adjacent floats, call them equal. (http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/) 130 | return (diff < -1 ? -1 : (diff > 1 ? +1 : 0)); 131 | } 132 | #endif 133 | } 134 | 135 | // Note that default comparison works fine for double -- doubles round-trip correctly through LitJson, because it uses 136 | // double as the primary input type for floating point numbers. 137 | private int CompareDouble(double x, double y) 138 | { 139 | return Comparer.Default.Compare(x, y); 140 | } 141 | 142 | private int CompareDecimal(decimal x, decimal y) 143 | { 144 | #if false 145 | // TODO: this is what we should be doing, because decimal is designed for precise repeatability and exact equality. 146 | // However this doesn't work at present, because LitJson parses decimal strings as doubles, which loses precision. 147 | return Comparer.Default.Compare(x, y); 148 | #else 149 | if (x == y) 150 | return 0; 151 | 152 | // In the meantime, convert to double and settle for some loss of precision ... 153 | // Jump through unsafe hoops to compare via binary representation of floating point numbers. 154 | double xd = (double)x; 155 | double yd = (double)y; 156 | unsafe 157 | { 158 | long xi = *(long*)&xd; 159 | long yi = *(long*)&yd; 160 | long diff = xi - yi; 161 | 162 | // Note that we can't be as precise as above with doubles. 163 | return (diff < -16 ? -1 : (diff > 16 ? +1 : 0)); 164 | } 165 | #endif 166 | } 167 | 168 | // Random floating point numbers don't really add much confidence, better to explicitly test 169 | // specific edge cases and (logarithmic?) ranges. 170 | private void RoundTripFloatRandom(System.Comparison compare, double min, double max) 171 | { 172 | const int n = 10000; 173 | 174 | Random rand = new Random(0x1337); 175 | for (int i = 0; i < n; ++i) 176 | { 177 | // Add carefully, to avoid overflowing max range. 178 | double d = rand.NextDouble(); 179 | double num = d * max + (1.0 - d) * min; 180 | T tnum = (T)Convert.ChangeType(num, typeof(T)); 181 | RoundTrip(compare, false, tnum); 182 | } 183 | } 184 | 185 | private void RoundTrip(params T[] input) 186 | { 187 | RoundTrip(Comparer.Default.Compare, true, input); 188 | } 189 | 190 | private void RoundTrip(System.Comparison compare, bool writeJsonToConsole, params T[] input) 191 | { 192 | string json = JsonMapper.ToJson(input); 193 | if (writeJsonToConsole) 194 | { 195 | Console.WriteLine(json); 196 | } 197 | T[] output = JsonMapper.ToObject(json); 198 | 199 | Assert.AreEqual(input.Length, output.Length); 200 | for (int i = 0; i < input.Length; ++i) 201 | { 202 | Assert.That(compare(input[i], output[i]) == 0, "{0} does not match {1}", input[i], output[i]); 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /test/JsonTypeHintingTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace LitJson.Test { 7 | 8 | class TestTypedPrimitiveFieldPoly { 9 | public object field; 10 | } 11 | 12 | class TestTypedPrimitivePropPoly { 13 | public object prop; 14 | } 15 | 16 | [TestFixture] 17 | public class JsonTypeHintingTest { 18 | 19 | [Test] 20 | public void test_primitive_field_polymorphism() { 21 | var input = new TestTypedPrimitiveFieldPoly(); 22 | input.field = "string"; 23 | JsonWriter writer = new JsonWriter(); 24 | writer.TypeHinting = true; 25 | JsonMapper.ToJson(input, writer); 26 | string json = writer.ToString(); 27 | Assert.IsNotNull(json); 28 | JsonReader reader = new JsonReader(json); 29 | reader.TypeHinting = true; 30 | var output = JsonMapper.ToObject(reader); 31 | Assert.IsNotNull(output); 32 | Assert.AreEqual("string", output.field); 33 | } 34 | 35 | [Test] 36 | public void test_primitive_property_polymorphism() { 37 | var input = new TestTypedPrimitivePropPoly(); 38 | input.prop = "string"; 39 | JsonWriter writer = new JsonWriter(); 40 | writer.TypeHinting = true; 41 | JsonMapper.ToJson(input, writer); 42 | string json = writer.ToString(); 43 | Assert.IsNotNull(json); 44 | JsonReader reader = new JsonReader(json); 45 | reader.TypeHinting = true; 46 | var output = JsonMapper.ToObject(reader); 47 | Assert.IsNotNull(output); 48 | Assert.AreEqual("string", output.prop); 49 | } 50 | 51 | [Test] 52 | public void test_polymorphic_primitive_generic_list() { 53 | var input = new List(); 54 | input.Add("string"); 55 | input.Add(1); 56 | input.Add(new object()); 57 | input.Add(1.0f); 58 | JsonWriter writer = new JsonWriter(); 59 | writer.TypeHinting = true; 60 | JsonMapper.ToJson(input, writer); 61 | string json = writer.ToString(); 62 | Assert.IsNotNull(json); 63 | JsonReader reader = new JsonReader(json); 64 | reader.TypeHinting = true; 65 | var output = JsonMapper.ToObject>(reader); 66 | Assert.IsNotNull(output); 67 | Assert.AreEqual(4, output.Count); 68 | Assert.AreEqual("string", output[0]); 69 | Assert.AreEqual(1, output[1]); 70 | Assert.AreEqual(typeof(object), output[2].GetType()); 71 | Assert.AreEqual(1.0f, output[3]); 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /test/JsonWriterTest.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | /** 3 | * JsonWriterTest.cs 4 | * Tests for the JsonWriter class. 5 | * 6 | * The authors disclaim copyright to this source code. For more details, see 7 | * the COPYING file included with this distribution. 8 | **/ 9 | #endregion 10 | 11 | 12 | using LitJson; 13 | using NUnit.Framework; 14 | using System; 15 | using System.IO; 16 | using System.Text; 17 | 18 | 19 | namespace LitJson.Test 20 | { 21 | [TestFixture] 22 | public class JsonWriterTest 23 | { 24 | [Test] 25 | public void BooleansTest () 26 | { 27 | JsonWriter writer = new JsonWriter(); 28 | 29 | writer.WriteArrayStart(); 30 | writer.Write(true); 31 | writer.Write(false); 32 | writer.Write(false); 33 | writer.Write(true); 34 | writer.WriteArrayEnd(); 35 | } 36 | 37 | [Test] 38 | [ExpectedException (typeof(JsonException))] 39 | public void ErrorExcessDataTest () 40 | { 41 | JsonWriter writer = new JsonWriter(); 42 | 43 | writer.WriteArrayStart(); 44 | writer.Write(true); 45 | writer.WriteArrayEnd(); 46 | writer.Write(false); 47 | } 48 | 49 | [Test] 50 | [ExpectedException (typeof(JsonException))] 51 | public void ErrorArrayClosingTest () 52 | { 53 | JsonWriter writer = new JsonWriter(); 54 | 55 | writer.WriteArrayStart(); 56 | writer.Write(true); 57 | writer.WriteObjectEnd(); 58 | } 59 | 60 | [Test] 61 | [ExpectedException (typeof(JsonException))] 62 | public void ErrorNoArrayOrObjectTest () 63 | { 64 | JsonWriter writer = new JsonWriter(); 65 | 66 | writer.Write(true); 67 | } 68 | 69 | [Test] 70 | [ExpectedException (typeof(JsonException))] 71 | public void ErrorObjectClosingTest () 72 | { 73 | JsonWriter writer = new JsonWriter(); 74 | 75 | writer.WriteObjectStart(); 76 | writer.WritePropertyName("foo"); 77 | writer.Write("bar"); 78 | writer.WriteArrayEnd(); 79 | } 80 | 81 | [Test] 82 | [ExpectedException (typeof(JsonException))] 83 | public void ErrorPropertyExpectedTest () 84 | { 85 | JsonWriter writer = new JsonWriter(); 86 | 87 | writer.WriteObjectStart(); 88 | writer.Write(10); 89 | writer.WriteObjectEnd(); 90 | } 91 | 92 | [Test] 93 | [ExpectedException (typeof(JsonException))] 94 | public void ErrorValueExpectedTest () 95 | { 96 | JsonWriter writer = new JsonWriter(); 97 | 98 | writer.WriteObjectStart(); 99 | writer.WritePropertyName("foo"); 100 | writer.WriteObjectEnd(); 101 | } 102 | 103 | [Test] 104 | public void NestedArraysTest () 105 | { 106 | JsonWriter writer = new JsonWriter(); 107 | 108 | string json = "[1,[\"a\",\"b\",\"c\"],2,[[null]],3]"; 109 | 110 | writer.WriteArrayStart(); 111 | writer.Write(1); 112 | writer.WriteArrayStart(); 113 | writer.Write("a"); 114 | writer.Write("b"); 115 | writer.Write("c"); 116 | writer.WriteArrayEnd(); 117 | writer.Write(2); 118 | writer.WriteArrayStart(); 119 | writer.WriteArrayStart(); 120 | writer.Write(null); 121 | writer.WriteArrayEnd(); 122 | writer.WriteArrayEnd(); 123 | writer.Write(3); 124 | writer.WriteArrayEnd(); 125 | 126 | Assert.AreEqual(writer.ToString (), json); 127 | } 128 | 129 | [Test] 130 | public void NestedObjectsTest () 131 | { 132 | JsonWriter writer = new JsonWriter(); 133 | 134 | string json = "{\"book\":{\"title\":" + 135 | "\"Structure and Interpretation of Computer Programs\"," + 136 | "\"details\":{\"pages\":657}}}"; 137 | 138 | writer.WriteObjectStart(); 139 | writer.WritePropertyName("book"); 140 | writer.WriteObjectStart(); 141 | writer.WritePropertyName("title"); 142 | writer.Write( 143 | "Structure and Interpretation of Computer Programs"); 144 | writer.WritePropertyName("details"); 145 | writer.WriteObjectStart(); 146 | writer.WritePropertyName("pages"); 147 | writer.Write(657); 148 | writer.WriteObjectEnd(); 149 | writer.WriteObjectEnd(); 150 | writer.WriteObjectEnd(); 151 | 152 | Assert.AreEqual(writer.ToString (), json); 153 | } 154 | 155 | [Test] 156 | [ExpectedException (typeof(ArgumentNullException))] 157 | public void NullWriterTest () 158 | { 159 | TextWriter text_writer = null; 160 | JsonWriter writer = new JsonWriter(text_writer); 161 | 162 | writer.Write(123); 163 | } 164 | 165 | [Test] 166 | public void NullTest () 167 | { 168 | JsonWriter writer = new JsonWriter(); 169 | 170 | writer.WriteArrayStart(); 171 | writer.Write(null); 172 | writer.WriteArrayEnd(); 173 | } 174 | 175 | [Test] 176 | public void NullTextWriterTest () 177 | { 178 | JsonWriter writer = new JsonWriter(TextWriter.Null); 179 | 180 | writer.WriteArrayStart(); 181 | writer.Write("Hello"); 182 | writer.Write("World"); 183 | writer.WriteArrayEnd(); 184 | } 185 | 186 | [Test] 187 | public void NumbersTest () 188 | { 189 | JsonWriter writer = new JsonWriter(); 190 | 191 | writer.WriteArrayStart(); 192 | writer.Write(0); 193 | writer.Write(100); 194 | writer.Write((byte) 200); 195 | writer.Write(-256); 196 | writer.Write(10000000000L); 197 | writer.Write((double) 0.333); 198 | writer.Write((float) 0.0001); 199 | writer.Write(9e-20); 200 | writer.Write(2.3e8); 201 | writer.Write(Math.PI); 202 | writer.WriteArrayEnd(); 203 | } 204 | 205 | [Test] 206 | public void ObjectTest () 207 | { 208 | JsonWriter writer = new JsonWriter(); 209 | 210 | string json = "{\"flavour\":\"strawberry\",\"color\":\"red\"," + 211 | "\"amount\":3}"; 212 | 213 | writer.WriteObjectStart(); 214 | writer.WritePropertyName("flavour"); 215 | writer.Write("strawberry"); 216 | writer.WritePropertyName("color"); 217 | writer.Write("red"); 218 | writer.WritePropertyName("amount"); 219 | writer.Write(3); 220 | writer.WriteObjectEnd(); 221 | 222 | Assert.AreEqual(writer.ToString (), json); 223 | } 224 | 225 | [Test] 226 | public void PrettyPrintTest () 227 | { 228 | JsonWriter writer = new JsonWriter(); 229 | 230 | string json = @" 231 | [ 232 | { 233 | ""precision"" : ""zip"", 234 | ""Latitude"" : 37.7668, 235 | ""Longitude"" : -122.3959, 236 | ""City"" : ""SAN FRANCISCO"" 237 | }, 238 | { 239 | ""precision"" : ""zip"", 240 | ""Latitude"" : 37.371991, 241 | ""Longitude"" : -122.02602, 242 | ""City"" : ""SUNNYVALE"" 243 | } 244 | ]"; 245 | 246 | writer.PrettyPrint = true; 247 | 248 | writer.WriteArrayStart(); 249 | writer.WriteObjectStart(); 250 | writer.WritePropertyName("precision"); 251 | writer.Write("zip"); 252 | writer.WritePropertyName("Latitude"); 253 | writer.Write(37.7668); 254 | writer.WritePropertyName("Longitude"); 255 | writer.Write(-122.3959); 256 | writer.WritePropertyName("City"); 257 | writer.Write("SAN FRANCISCO"); 258 | writer.WriteObjectEnd(); 259 | 260 | writer.IndentValue = 2; 261 | 262 | writer.WriteObjectStart(); 263 | writer.WritePropertyName("precision"); 264 | writer.Write("zip"); 265 | writer.WritePropertyName("Latitude"); 266 | writer.Write(37.371991); 267 | writer.WritePropertyName("Longitude"); 268 | writer.Write(-122.02602); 269 | writer.WritePropertyName("City"); 270 | writer.Write("SUNNYVALE"); 271 | writer.WriteObjectEnd(); 272 | writer.WriteArrayEnd(); 273 | 274 | Assert.AreEqual(json, writer.ToString ()); 275 | } 276 | 277 | [Test] 278 | public void StringBuilderTest () 279 | { 280 | StringBuilder sb = new StringBuilder (); 281 | JsonWriter writer = new JsonWriter(sb); 282 | 283 | writer.WriteArrayStart(); 284 | writer.Write("like a lizard on a window pane"); 285 | writer.WriteArrayEnd(); 286 | 287 | Assert.AreEqual(sb.ToString (), 288 | "[\"like a lizard on a window pane\"]"); 289 | } 290 | 291 | [Test] 292 | public void StringsTest () 293 | { 294 | JsonWriter writer = new JsonWriter(); 295 | 296 | writer.WriteArrayStart(); 297 | writer.Write("Hello World!"); 298 | writer.Write("\n\r\b\f\t"); 299 | writer.Write("I \u2665 you"); 300 | writer.Write("She said, \"I know what it's like to be dead\""); 301 | writer.WriteArrayEnd(); 302 | 303 | string json = 304 | "[\"Hello World!\",\"\\n\\r\\b\\f\\t\",\"I \\u2665 you\"" + 305 | ",\"She said, \\\"I know what it's like to be dead\\\"\"]"; 306 | 307 | Assert.AreEqual(json, writer.ToString(), "A1"); 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /test/json-example.txt: -------------------------------------------------------------------------------- 1 | { 2 | "web-app": { 3 | "servlet": [ 4 | { 5 | "servlet-name": "cofaxCDS", 6 | "servlet-class": "org.cofax.cds.CDSServlet", 7 | "init-param": { 8 | "configGlossary:installationAt": "Philadelphia, PA", 9 | "configGlossary:adminEmail": "ksm@pobox.com", 10 | "configGlossary:poweredBy": "Cofax", 11 | "configGlossary:poweredByIcon": "/images/cofax.gif", 12 | "configGlossary:staticPath": "/content/static", 13 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 14 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 15 | "templatePath": "templates", 16 | "templateOverridePath": "", 17 | "defaultListTemplate": "listTemplate.htm", 18 | "defaultFileTemplate": "articleTemplate.htm", 19 | "useJSP": false, 20 | "jspListTemplate": "listTemplate.jsp", 21 | "jspFileTemplate": "articleTemplate.jsp", 22 | "cachePackageTagsTrack": 200, 23 | "cachePackageTagsStore": 200, 24 | "cachePackageTagsRefresh": 60, 25 | "cacheTemplatesTrack": 100, 26 | "cacheTemplatesStore": 50, 27 | "cacheTemplatesRefresh": 15, 28 | "cachePagesTrack": 200, 29 | "cachePagesStore": 100, 30 | "cachePagesRefresh": 10, 31 | "cachePagesDirtyRead": 10, 32 | "searchEngineListTemplate": "forSearchEnginesList.htm", 33 | "searchEngineFileTemplate": "forSearchEngines.htm", 34 | "searchEngineRobotsDb": "WEB-INF/robots.db", 35 | "useDataStore": true, 36 | "dataStoreClass": "org.cofax.SqlDataStore", 37 | "redirectionClass": "org.cofax.SqlRedirection", 38 | "dataStoreName": "cofax", 39 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 40 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 41 | "dataStoreUser": "sa", 42 | "dataStorePassword": "dataStoreTestQuery", 43 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 44 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 45 | "dataStoreInitConns": 10, 46 | "dataStoreMaxConns": 100, 47 | "dataStoreConnUsageLimit": 100, 48 | "dataStoreLogLevel": "debug", 49 | "maxUrlLength": 500 50 | } 51 | }, 52 | { 53 | "servlet-name": "cofaxEmail", 54 | "servlet-class": "org.cofax.cds.EmailServlet", 55 | "init-param": { 56 | "mailHost": "mail1", 57 | "mailHostOverride": "mail2" 58 | } 59 | }, 60 | { 61 | "servlet-name": "cofaxAdmin", 62 | "servlet-class": "org.cofax.cds.AdminServlet" 63 | }, 64 | 65 | { 66 | "servlet-name": "fileServlet", 67 | "servlet-class": "org.cofax.cds.FileServlet" 68 | }, 69 | { 70 | "servlet-name": "cofaxTools", 71 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 72 | "init-param": { 73 | "templatePath": "toolstemplates/", 74 | "log": 1, 75 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 76 | "logMaxSize": "", 77 | "dataLog": 1, 78 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 79 | "dataLogMaxSize": "", 80 | "removePageCache": "/content/admin/remove?cache=pages&id=", 81 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 82 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 83 | "lookInContext": 1, 84 | "adminGroupID": 4, 85 | "betaServer": true 86 | } 87 | } 88 | ], 89 | 90 | "servlet-mapping": { 91 | "cofaxCDS": "/", 92 | "cofaxEmail": "/cofaxutil/aemail/*", 93 | "cofaxAdmin": "/admin/*", 94 | "fileServlet": "/static/*", 95 | "cofaxTools": "/tools/*" 96 | }, 97 | 98 | "taglib": { 99 | "taglib-uri": "cofax.tld", 100 | "taglib-location": "/WEB-INF/tlds/cofax.tld" 101 | } 102 | } 103 | } 104 | --------------------------------------------------------------------------------