{"id":17955261,"url":"https://github.com/atifaziz/jacob","last_synced_at":"2025-03-25T01:31:12.654Z","repository":{"id":40452088,"uuid":"455550278","full_name":"atifaziz/Jacob","owner":"atifaziz","description":"A succinct and compositional .NET API for reading JSON","archived":false,"fork":false,"pushed_at":"2023-09-27T12:36:40.000Z","size":1519,"stargazers_count":8,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-16T16:15:36.342Z","etag":null,"topics":["hacktoberfest","json"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atifaziz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-04T13:05:31.000Z","updated_at":"2023-09-27T12:38:55.000Z","dependencies_parsed_at":"2024-10-29T11:03:07.278Z","dependency_job_id":null,"html_url":"https://github.com/atifaziz/Jacob","commit_stats":{"total_commits":137,"total_committers":3,"mean_commits":"45.666666666666664","dds":0.0948905109489051,"last_synced_commit":"51d40b418cba58d9f3223a8b86bf4c33df62e299"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atifaziz%2FJacob","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atifaziz%2FJacob/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atifaziz%2FJacob/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atifaziz%2FJacob/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atifaziz","download_url":"https://codeload.github.com/atifaziz/Jacob/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245381496,"owners_count":20606045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","json"],"created_at":"2024-10-29T10:25:38.453Z","updated_at":"2025-03-25T01:31:11.963Z","avatar_url":"https://github.com/atifaziz.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jacob\n\nJacob provides a succinct JSON combinator API over `Utf8JsonReader` for\ndeserializing/reading JSON data.\n\n.NET Core 3.0 introduced [`Utf8JsonReader`], a high-performance, low-allocation,\nforward-only reader for JSON text encoded in UTF-8. It is a low-level type for\nbuilding custom parsers and deserializers, and because it has _low-level_\nsemantics, its API is not very straightforward to use. It's far more common\ninstead to see developers create simple classes that mirror the JSON data and\nthen use [`JsonSerializer`] to deserialize/read the JSON data into instances of\nthose classes. However, when your application or domain object model is far\nricher than the value types available in JSON, you have two possible approaches:\n\n1. Customize the deserialization by writing and registering a [`JsonConverter`].\n   This requires a non-trivial amount of coding and understanding, not to\n   mention having to work with `Utf8JsonReader` and writing unit tests for each\n   converter. Moreover, the converters are not immediately reusable and\n   composable (in an ad-hoc manner or otherwise) without considerable effort.\n\n2. Maintain two sets of object models: one that is usually anemic (containing\n   very little logic) and for the sole purpose of deserializing from JSON, and\n   another that is the rich application/domain model. Then, have logic to\n   transform the former into the latter. This requires extra processing as well\n   as additional and temporary intermediate represetations of the data in\n   memory.\n\nJacob provides most of the benefits of `Utf8JsonReader`, such as low-allocation,\nbut without all the ceremony and complexity associated with either of the above\napproaches.\n\n\n## Usage\n\nAll of the following examples assume these imports:\n\n```c#\nusing System;\nusing System.Collections.Generic;\nusing System.Text.Json;\nusing Jacob;\n```\n\nStarting simple, here is how to read a JSON number as an `int` in C# (or\n`System.Int32`):\n\n```c#\nvar x = JsonReader.Int32().Read(\"42\");\nConsole.WriteLine(x);\n```\n\nHere is how to read a JSON string as a .NET string:\n\n```c#\nvar s = JsonReader.String().Read(\"\"\" \"foobar\" \"\"\");\nConsole.WriteLine(s);\n```\n\nDeserializing simple types like a string or an integer is pretty straightforward\nand can be done with [`JsonSerializer.Deserialize`][deserialize] with equal\nease:\n\n```c#\nvar x = JsonSerializer.Deserialize\u003cint\u003e(\"42\");\nConsole.WriteLine(x);\n\nvar s = JsonSerializer.Deserialize\u003cstring\u003e(\"\"\" \"foobar\" \"\"\");\nConsole.WriteLine(s);\n```\n\nHowever, the next example shows the benefits of a compositional API. It\ndemonstrates how to read a tuple of string and integer expressed in JSON as\nan array of two elements:\n\n```c#\nvar (key, value) =\n    JsonReader.Tuple(JsonReader.String(), JsonReader.Int32())\n              .Read(\"\"\"[\"foobar\", 42]\"\"\");\nConsole.WriteLine($\"Key = {key}, Value = {value}\");\n```\n\nNote how `JsonReader.Tuple` builds on top of the string and integer reader\nintroduced in previous examples. By supplying those readers as arguments, the\ntuple reader then knows how to read each item of the tuple. The tuple reader\nalso knows to expect only a JSON array of two elements; if more elements are\nsupplied, as in `[\"foobar\", 42, null]`, then it will produce the following\nerror:\n\n    Invalid JSON value; JSON array has too many values. See token \"Null\" at offset 13.\n\nAttempting to do the same with `JsonSerializer.Deserialize` (assuming .NET 6):\n\n```c#\nvar json = \"\"\"[\"foobar\", 42]\"\"\";\nvar (key, value) = JsonSerializer.Deserialize\u003c(string, int)\u003e(json);\nConsole.WriteLine($\"Key = {key}, Value = {value}\");\n```\n\nfails with the error:\n\n    The JSON value could not be converted to System.ValueTuple`2[System.String,System.Int32]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.\n\nand this is where a custom [`JsonConverter`] implementation would be needed that\nknows how to deserialize a tuple of a string and an integer from a JSON array.\n\nOnce a reader is initialized, it can be reused. In the next example, the reader\nis defined outside the loop and then used to read JSON through each iteration:\n\n```c#\nvar reader = JsonReader.Tuple(JsonReader.String(), JsonReader.Int32());\n\nforeach (var json in new[]\n                     {\n                         \"\"\"[\"foo\", 123]\"\"\",\n                         \"\"\"[\"bar\", 456]\"\"\",\n                         \"\"\"[\"baz\", 789]\"\"\",\n                     })\n{\n    var (key, value) = reader.Read(json);\n    Console.WriteLine($\"Key = {key}, Value = {value}\");\n}\n```\n\nJacob also enables use of LINQ so you can read a value as one type and project\nit to a value of another type that may be closer to what the application might\ndesire:\n\n```c#\nvar reader =\n    from t in JsonReader.Tuple(JsonReader.String(), JsonReader.Int32())\n    select KeyValuePair.Create(t.Item1, t.Item2);\n\nvar pair = reader.Read(\"\"\"[\"foobar\", 42]\"\"\");\nConsole.WriteLine(pair);\n```\n\nIn the above example, a tuple `(string, int)` is converted to a\n`KeyValuePair\u003cstring, int\u003e`. Again, this demonstrates how readers just compose\nwith one another. In the same vein, if an array of key-value pairs is what's\nneeded, then it's just another composition away (this time, with\n`JsonReader.Array`):\n\n```c#\nvar pairReader =\n    from t in JsonReader.Tuple(JsonReader.String(), JsonReader.Int32())\n    select KeyValuePair.Create(t.Item1, t.Item2);\n\nconst string json = \"\"\"\n    [\n        [\"foo\", 123],\n        [\"bar\", 456],\n        [\"baz\", 789]\n    ]\n    \"\"\";\n\nvar pairs = JsonReader.Array(pairReader).Read(json);\n\nforeach (var (key, value) in pairs)\n    Console.WriteLine($\"Key = {key}, Value = {value}\");\n```\n\n`JsonReader.Either` allows a JSON value to be read in one of two ways. For\nexample, here a JSON array is read whose elements are either an integer\nor a string:\n\n```c#\nvar reader =\n    JsonReader.Either(JsonReader.String().AsObject(),\n                      JsonReader.Int32().AsObject());\n\nconst string json = \"\"\"[\"foo\", 123, \"bar\", 456, \"baz\", 789]\"\"\";\n\nvar values = JsonReader.Array(reader).Read(json);\n\nforeach (var value in values)\n    Console.WriteLine($\"{value} ({value.GetType().Name})\");\n\n/* outputs:\n\nfoo (String)\n123 (Int32)\nbar (String)\n456 (Int32)\nbaz (String)\n789 (Int32)\n*/\n```\n\n`Either` expects either reader to return the same type of value, which is why\n`AsObject()` is used to convert each read string or integer to the common super\ntype `object`. `Either` can also be combined with itself to support reading a\nJSON value in more than two ways.\n\nFinally, there's `JsonReader.Object` that takes property definitions via\n`JsonReader.Property` and a function to combine the read values into the final\nobject:\n\n```c#\nvar pairReader =\n    JsonReader.Object(\n        JsonReader.Property(\"key\", JsonReader.String()),\n        JsonReader.Property(\"value\", JsonReader.Int32()),\n        KeyValuePair.Create /*\n        above is same as:\n        (k, v) =\u003e KeyValuePair.Create(k, v) */\n    );\n\nconst string json = \"\"\"\n    [\n        { \"key\"  : \"foo\", \"value\": 123   },\n        { \"value\": 456  , \"key\"  : \"bar\" },\n        { \"key\"  : \"baz\", \"value\": 789   }\n    ]\n    \"\"\";\n\nvar pairs = JsonReader.Array(pairReader).Read(json);\n\nforeach (var (key, value) in pairs)\n    Console.WriteLine($\"Key = {key}, Value = {value}\");\n```\n\nOnce more, `JsonReader.Object` builds on top of readers for each property for\na combined effect of creating an object from the constituent parts.\n\n\n## Partial JSON Reading\n\nReading partial JSON is supported by most readers. It enables large JSON text\ndata to be read and processed in chunks without committing it entirely to\nmemory.\n\nAll readers support `TryRead`, which returns a `JsonReadResult\u003cT\u003e` that either\nrepresents the read value or an error with reading the value. When JSON data\nis partially loaded in a buffer such that a reader cannot complete its reading\nthen the `Incomplete` property of the returned `JsonReadResult\u003cT\u003e` will be\n`true`. This is a signal to the caller that it must load the buffer with more\nof the JSON source text to resume reading.\n\nAll readers can be composed with `JsonReader.Buffer` to load enough\ndata into the buffer so that none can fail due to partial JSON.\n`JsonReader.Buffer` ensures that at least one complete JSON value (be that a\nscalar like a string or a structure like an array or an object) is buffered.\n\n\n## Limitations\n\n- There is no API at this time for writing JSON data.\n\n- When using `JsonReader.Object` with `JsonReader.Property`, a maximum of 16\n  properties of a JSON object can be read.\n\n\n[`Utf8JsonReader`]: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter?pivots=dotnet-6-0#use-utf8jsonreader\n[`JsonSerializer`]: https://docs.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer\n[`JsonConverter`]: https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter?view=net-6.0\n[x-strict-json]: https://github.com/JamesNK/Newtonsoft.Json/issues/646#issuecomment-356194475\n[deserialize]: https://docs.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer.deserialize?view=net-6.0\n[Newtonsoft.Json (13.x)]: https://www.nuget.org/packages/Newtonsoft.Json/13.0.1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatifaziz%2Fjacob","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatifaziz%2Fjacob","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatifaziz%2Fjacob/lists"}