{"id":46605177,"url":"https://github.com/faceless2/json","last_synced_at":"2026-03-07T17:15:52.480Z","repository":{"id":108690842,"uuid":"157420871","full_name":"faceless2/json","owner":"faceless2","description":"Java JSON, CBOR, Msgpack, JWT, COSE toolkit","archived":false,"fork":false,"pushed_at":"2026-03-04T18:25:25.000Z","size":30605,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-05T00:47:15.527Z","etag":null,"topics":["cbor","cose","java","json","jwk","jwt","msgpack"],"latest_commit_sha":null,"homepage":"https://faceless2.github.io/json","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/faceless2.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-11-13T17:38:09.000Z","updated_at":"2026-03-04T18:27:37.000Z","dependencies_parsed_at":"2024-09-16T21:35:38.198Z","dependency_job_id":"cfd1de9b-3144-4673-8a91-c492f4808f5a","html_url":"https://github.com/faceless2/json","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/faceless2/json","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceless2%2Fjson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceless2%2Fjson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceless2%2Fjson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceless2%2Fjson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faceless2","download_url":"https://codeload.github.com/faceless2/json/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceless2%2Fjson/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30222780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T17:00:40.062Z","status":"ssl_error","status_checked_at":"2026-03-07T17:00:39.026Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cbor","cose","java","json","jwk","jwt","msgpack"],"created_at":"2026-03-07T17:15:51.784Z","updated_at":"2026-03-07T17:15:52.474Z","avatar_url":"https://github.com/faceless2.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Java JSON, CBOR, Msgpack, JWT, COSE toolkit \n\nThe BFO JSON package is yet another Java JSON parser.\nIt supports JSR353 / JSR374 (both the `javax.json` and the `jakarta.json` packages),\nand also has a custom API\nwhich adds support for Msgpack, CBOR and CBOR-diag (the `com.bfo.json` package).\n\n### simple\n* the `com.bfo.json` API is essentially a single class, with a few helper classes that are all optional.\nItems are added with `put`, retrieved with `get`, read with `read` and written with `write`.\nCollections are used for maps and lists, and you can use the whole API with no more than\nabout 5 or 6 methods.\nWhich means although the API is [fully documented](https://faceless2.github.io/json/docs/), you can probably get away without reading any of it.\n\n### fast\n* A 2021 Macbook M1 will read Json at about 120MB/s from text (80MB/s from binary, as it has to convert to UTF-8),\nand write at about 400MB/s. It can read CBOR/Msgpack at about 300MB/s and write at 600MB/s. That's ridiculously fast;\nas part of version 1.4 I had to change the large-file benchmarks to use microseconds. A great deal of effort has been\nspent on removing buffer copying and avoiding slow codepaths in the JVM - I don't think you'll find a faster Java API.\n\n### streaming (new in v2)\n* As of version 2 reading is block-based. A Reader can be created, and blocks fed into it\nas they arrive. This is new in version 2.\n\n### correct\n* the API has been tested against the Json sample data made available by Nicolas Seriot at\nhttp://seriot.ch/parsing_json.php,\nand has been authored with reference to [RFC8259](https://tools.ietf.org/html/rfc8259).\nCBOR support is newer, but has again been tested against [RFC7049](https://tools.ietf.org/html/rfc7049)\nand fuzzed input, to make sure errors are handled properly. It's also been run against the\nJSR374 test suite.\n\n### self-contained\n* the API has no external requirements unless you're using the `javax.json` package, in which case you'll need the Jar\ncontaining those classes.\n\n## Features\n* JSON, CBOR, Msgpack and CBOR-diag serialization are all available from the same object; read as one format, write as another.\n* Map keys can be numbers or booleans as well as strings - required for COSE/Msgpack, these will be converted to strings when serializing to JSON\n* Listeners and Events to monitor changes to the structure\n* Easy interchange between the `com.bfo.json` and `javax.json` APIs without needing to serialize.\n* Flexible typing; if you request the int value of a string it will try to convert it to an int. If you put a value with a String key on a list, it will convert to a map.\n* Numbers read as ints, longs, doubles, BigIntegers, or BigDecimals, with the smallest type chosen first.\n* CBOR/Msgpack binary strings are stored as ByteBuffers, but will be converted to Base64 strings when serialized as Json.\n* Option of mapping Json to more complex Java objects is possible, but not included with the code. By default data is retrieved as  Maps, Lists and primitive types only\n* Java Web Token (JWT) support class for reading/writing/signing/verifying.\n* COSE signed object support class for reading/writing/signing/verifying.\n* Java Web Keys (JWK) support EC, RSA and EdDSA public/private keys, and Hmac, AES-KW and AES-GCM-KW symmetric keys\n* Java Web Keys (JWK) support for ML-DSA (provisional) and SLH-DSA (extremely provisional) - both new in 2.1\n* Experimental Yaml parser (derived from https://github.com/EsotericSoftware/yamlbeans)\n\n## Building and Documentation\n* Prebuilt binary available at [https://faceless2.github.io/json/dist/bfojson-2.1.jar](https://faceless2.github.io/json/dist/bfojson-2.1.jar)\n* The API docs will always be available at [https://faceless2.github.io/json/docs/](https://faceless2.github.io/json/docs/)\n* Compiles under Java 11 or later - the API supports EdDSA keys (new in Java 15) via reflection.\n* Or download with `git clone http://github.com/faceless2/json.git`. Type `ant`. Jar is in `dist`, docs are in `docs`\n \n## Design Decisions\n* Json objects have a _parent_ pointer, which means they can only exist in the tree at one location.\n  They are directly mutable, no need for builder classes.\n\n* Mapping JavaScript objects to a Java object can be done by use of a JsonFactory, however this is done after the object is read.\n  Most of the complexity of other Java Json APIs comes from the mapping process between Json and Java objects;\n  if you want to go down this route you have a trivially simple interface to implement, but you're on your own.\n   \n* JavaScript is loosely typed, and this API acknowleges this: if you request an int value from a Json string,\n  it will try to parse the String as an integer. If you don't want this, see the JsonReadOptions to turn it off.\n\n* Json in the wild has many variations - comments are embedded, maps have unquoted keys, and so on.\n  By default the API will adhere closely to RFC8259 when reading or writing, although this can be changed.\n  Again see JsonReadOptions.\n\n* When reading Json numbers, they will be mapped to ints, longs, BigIntegers and double as necessary.\n  If BigDecimal support is required, this can be turned on in JsonReadOptions\n\n* Json is read from `java.io.Reader` and written to `java.io.Appendable`.\n  You can read from an `InputStream` too, in which case it will look for a UTF-8 or UTF-16 BOM at the start of the stream.\n  CBOR/Msgpack are read from an `InputStream` and written to an `OutputStream`.\n  Errors encountered during reading are reported with context, line and column numbers (for JSON) or byte offset (for CBOR/Msgpack).\n\n* CBOR serialization offers two complexities that are not supported in this API:\n  duplicate keys in maps and \"special\" types that are not defined.\n  Duplicate keys encountered during reading throw an `IOException`,\n  and special types (which should really only be encountered while testing)\n  are converted to a tagged null object. Tags are limited\n  to 63 bits, and tags applied to Map keys are ignored.\n\n* CBOR serialization will convert tag types 2 and 3 on a \"buffer\" to BigInteger, as described in RFC7049.\n  But other tags used to distinguish Dates, non-UTF8 strings, URLs etc. are not applied.\nA \u003ccode\u003eJsonFactory\u003c/code\u003e can easily be written to cover as many of these are needed.\n\n* Msgpack serialization is similar to CBOR, but simpler. \"extension types\" are stored as\n  Buffers, with the extension type stored as a tag from 0..255. Like CBOR, duplicate keys encountered\n  during read will throw an IOException.\n\n* It's possible (since 1.4) to read and write indefinitely large strings and buffers - the [JsonReadOptions.Filter](https://faceless2.github.io/json/docs/api/com/bfo/json/JsonReadOptions.Filter.html) class can be used to divert content away to a File, for example. The use of intermediate buffers has been kept to an absolute minimum.\n\n* Reading Json/CBOR/Msgpack (since 2.0) is block based, to allow reading from packet-based APIs like `java.nio.channels.Selector`\n  or Netty.\n  \n\n## Examples\n```java\n// The basics\nJson json = Json.read(\"{}\"}; // Create a new map\njson.put(\"a\", \"apples\"); // Add a string\njson.putPath(\"b.c\", new Json(\"oranges\")); // Add an intermediate map and another string\njson.putPath(\"b.c[1]\", 3}; // Replace previous string with an array, add a null then a number.\njson.putPath(\"\\\"d.e\\\"\", true); // Add a key containing a quote character\nSystem.out.println(json); // {\"a\":\"apples\",\"b\":{\"c\":[null,3]},\"d.e\":true}\njson.write(System.out); // The same as above, but doesn't serialize to a String first.\njson.write(new JsonWriter().setOutput(System.out)); // Same as above, for setting writer options\nSystem.out.println(json.get(\"b\").get(\"c\").get(1).stringValue()); // \"3\"\nSystem.out.println(json.get(\"b\").get(\"c\").get(1).intValue()); // 3\nSystem.out.println(json.getPath(\"b.c[1]\").stringValue()); // \"3\" - same as above but using path\nSystem.out.println(json.getPath(\"b.c[1]\").intValue()); // 3 - same as above but using path\n\n// Types\njson.put(\"d\", \"2\");\njson.put(\"e\", 0);\nSystem.out.println(json.type()); // \"map\"\nSystem.out.println(json.isMap()); // true\nSystem.out.println(json.get(\"d\")); // \"2\"\nSystem.out.println(json.get(\"d\").type()); // \"string\"\nSystem.out.println(json.get(\"d\").intValue()); // 2 (automatically converted from string)\nSystem.out.println(json.get(\"d\").numberValue().getClass()); // java.lang.Integer\njson.put(\"d\", \"9999999999\");\nSystem.out.println(json.get(\"d\").numberValue().getClass()); // java.lang.Long\njson.put(\"d\", \"99999999999999999999999999\");\nSystem.out.println(json.get(\"d\").numberValue().getClass()); // java.math.BigInteger\nSystem.out.println(new Json(2).equals(new Json(2.0)));  // true - numbers compare by value not type\nSystem.out.println(json.get(\"d\").booleanValue()); // true\nSystem.out.println(json.get(\"e\").booleanValue()); // false\njson.put(\"e\", Json.read(\"[]\")); // Add a new list\nSystem.out.println(json.get(\"e\").type()); // \"list\"\njson.putPath(\"e[0]\", false);\nSystem.out.println(json.get(\"e\")); // [false] - e is a list\njson.putPath(\"e[\\\"a\\\"]\", true); // this will convert e to a map\nSystem.out.println(json.get(\"e\")); // {\"0\":false,\"a\":true}\njson.setValue(new Json(\"string\")); // copy value from specified object\nSystem.out.println(json.value()); // \"string\"\n\n// Serialization\nString input = \"{\\\"b\\\":1, /*comment*/ \\\"a\\\": 2}\";\njson = Json.read(input);  // Fails, comments not allowed\njson = Json.read(new JsonReader().setInput(new StringReader(input)).setComments(true)); // OK\njson.write(new JsonWriter().setOutput(...).setIndent(2).setSorted(true)); // pretty print and sort keys\n// {\n//   \"a\": 2,\n//   \"b\": 1,\n// }\n\n// CBOR / Msgpack\njson.put(\"buffer\", ByteBuffer.wrap(new byte[] { ... }));   // add a ByteBuffer\nSystem.out.println(json.get(\"buffer\").type());      // \"buffer\"\nSystem.out.println(json.get(\"buffer\").stringValue());  // Base64 encoded value of buffer\njson.setTag(20);        // Set a CBOR tag on a value\njson.write(new CborWriter().setOutput(outputstream));    // serialize the same JSON object to CBOR\njson = Json.read(new CborReader().setInput(inputstream));   // read CBOR from an Inputream\njson = Json.readCbor(inputstream);   // shortcut for the line above\nSystem.out.println(json.get(\"buffer\").getTag());        // \"20\"\nSystem.out.println(json.get(\"b\").getTag());        // \"-1\", which means no tag\njson.put(\"nan\", Double.NaN);\njson.write(new CborWriter().setOutput(outputstream));    // infinity is fine in CBOR\njson.write(new JsonWriter().setOutput(writer));    // throws IOException - infinity not allowed in Json\njson.write(new JsonWriter().setOutput(writer).setAllowNaN(true));  // infinity serializes as null\njson.write(new MsgpackWriter().setOutput(outputstream));    // Msgpack instead of CBOR\n\n\n// Events\njson.addListener(new JsonListener() {\n    public void jsonEvent(Json root, JsonEvent event) {\n        if (event.after == null) {\n            System.out.println(\"Removed \" + root.find(event.before));\n        } else if (event.before == null) {\n            System.out.println(\"Added \" + root.find(event.after));\n        } else {\n            System.out.println(\"Changed \" + root.find(event.after) + \" from \" + event.before+\" to \" + event.after);\n        }\n    }\n});\n\n// Paths as keys\njson.putPath(\"a.b\", true);  // \"Added a.b\"\njson.getPath(\"a.b\").put(\"c\", true);  // \"Added a.b.c\"\njson.getPath(\"a.b\").put(\"c\", false);  // \"Changed a.b.c\" from true to false\njson.removePath(\"a.b\"); // \"Removed a.b\"\n\n// Conversion to/from JSR374 - the representations are independent, not shared\njavax.json.JsonValue jsrvalue = javax.json.Json.createReader(...).readValue(); // object read via JSR374\nbfovalue = new Json(jsrvalue);   // convert from JSR374 to BFO\njsrvalue = javax.json.Json.createObjectBuilder(bfovalue.mapValue()).build(); // convert from BFO to JSR374\n\n// Factories for type conversion are simple\nJsonFactory factory = new JsonFactory() {\n    public Json toJson(Object o) {\n        if (o instanceof URL) {\n            return \"[url] \" + o;\n        }\n        return null;\n    }\n    public Object fromJson(Json o) {\n        if (o.isString() \u0026\u0026 o.startsWith(\"[url] \")) {\n            return new URL(o.stringValue().substring(6));\n        }\n        return null;\n    }\n};\nJson j = new Json(new URL(...), factory).toString(); // \"[url] ...\"\nJson j = Json.read(\"[url] ...\");    // read as normal\nj.setFactory(factory);              // ... then set factory on the tree.\nj.object();                         // ... factory ensures object is a URL.\nnew Json(Collections.\u003cString,URL\u003esingletonMap(key, url), factory); // collections work with factories too\n\n```\n\n### JWK and COSE\n\nIn addition the JWT class adds basic support for [Java Web Tokens](https://jwt.io).\nWith only a handful of methods it is trivial to use, but supports all JWT signing methods.\nEncryption with JWE is not supported\n\n```java\nJWT jwt = new JWT(Json.parse(\"{\\\"iss\\\":\\\"foo\\\"}\"));\nbyte[] secretkeydata = ...\nSecretKey key = new JWK(secretkeydata, \"HS256\").getSecretKey();\njwt.sign(key);                       // Sign using a symmetric key\njwt = new JWT(jwt.toString());       // Encode then decode\nassert jwt.verify(key);              // Verify using the same symmetric key\n\nPublicKey pubkey = ...\nPrivateKey prikey = ...\njwt.getHeader().put(\"x5u\", ...);     // Add custom content to header\njwt.sign(prikey);                    // Sign using a assymmetric key\nassert jwt.verify(pubkey);           // Verify using corresponding key\n\njwt.getPayload().clear();            // Modify the payload\nassert !jwt.verify(pubkey);          // Signature is no longer valid\n```\n\nCOSE was added in version 5. Signing with single or multiple signatures\nare supported, but counter-signing (for timestamps) is pending and encryption\nsupport is not currently planned.\n\n```java\n// Signing\nCOSE cose = new COSE();\ncose.setPayload(ByteBuffer.wrap(\"Hello, World\".getBytes(\"UTF-8\")));\ncose.sign(privateKey);               // Sign using a private key, eg ES256\ncose.writeCBOR(..., null);           // Write COSE to stream, or...\nByteBuffer buf = cose.toCbor();      // Write COSE to ByteBuffer\n\n// Verifying\nJson json = Json.readCBOR(..., null);     // Reload COSE\ncose = new COSE(json);\nString s = new String(cose.getPayload().array(), \"UTF-8\"); // Hello, World\nassert jwt.verify(publicKey) == 0;   // Verify with the public key\n```\n\nFor both JWT and COSE, the [JWK](https://faceless2.github.io/json/docs/com/bfo/json/JWK.html) utility class can convert\nbetween the Java `PublicKey`, `PrivateKey` and `SecretKey` implementations and their JWK or COSE-key representations.\n\nEdDSA requires Java 15+, ML-DSA requires Java 24+ or BouncyCastle 1.79+. However these\nare handled with reflection so the library can be compiled and run on Java 11 or later without any problems; the\nnew key types will throw an error if they're unsupported when encountered.\n\n# Related projects\n\n* https://github.com/faceless2/c2pa - a C2PA implementation built on this package\n* https://github.com/faceless2/zpath - an XPath-like language for JSON/CBOR.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaceless2%2Fjson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaceless2%2Fjson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaceless2%2Fjson/lists"}