GithubHelp home page GithubHelp logo

rex-json's Introduction

Rex Json

A simple cross-target JSON library for Reason/OCaml

Works with both native and js targets (compiled with bsb-native).

Why would you want this library?

  • you want minimal dependencies
  • you want forgiving json parsing (comments, trailing commas allowed)
  • you're fine with "good enough" performance

Installation

Add rex-json to your package.json and your bsconfig.json.

Usage:

let data = {|
{
  "some": "json", // with a comment!
  "more": [1,3,],
  /* also
  multi-line comment */
  "this": {
    "object": {
      "is": {
        "really": "nested"
      }
    }
  },
  "nested": [{
    "and": [1,2,{"stuff": 5}]
  }], // trailing commas!
}
|};
let json = Json.parse(data);
let simple = Json.get("some", json); /* == Some(String("json")) */

open Json.Infix;

let stuff = json
  |> Json.get("nested")
  |?> Json.nth(0)
  |?> Json.get("and")
  |?> Json.nth(2)
  |?> Json.get("stuff")
  |?> Json.number; /* == Some(5.) */

/** Using a json path for nested objects */
let nestedString = json
|> Json.getPath("this.object.is.really")
|?> Json.string; /* Some("nested") */

let str = Json.stringify(json); /* back to a string */
Js.log2(stuff, nestedString)

rex-json's People

Contributors

bsansouci avatar jaredly avatar mikaelbr avatar tesla3327 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

rex-json's Issues

Test suite

If you are interested, I found this test suite very useful for parsing JSON correctly: https://github.com/nst/JSONTestSuite/tree/master/test_parsing

// Beware, there's a lot of invisible characters in there
invalid("[1 true]");
invalid("[aÂ]");
invalid("[\"\": 1]");
invalid("[\"\"],");
invalid("[,1]");
invalid("[1,,2]");
invalid("[\"x\",,]");
invalid("[\"x\"]]");
invalid("[\"\",]");
invalid("[\"x\"");
invalid("[x");
invalid("[3[4]]");
invalid("[ˇ]");
invalid("[1:2]");
invalid("[,]");
invalid("[-]");
invalid("[ , \"\"]");
invalid("[\"a\", 4 ,1,");
invalid("[1,]");
invalid("[1,,]");
invalid("[\"�a\"\\f]");
invalid("[*]");
invalid("[\"\"");
invalid("[1,");
invalid("[1, 1 ,1");
invalid("[{}");
invalid("[fals]");
invalid("[nul]");
invalid("[tru]");
invalid("[++1234]");
invalid("[+1]");
invalid("[+Inf]");
invalid("[-01]");
invalid("[-1.0.]");
invalid("[-2.]");
invalid("[-NaN]");
invalid("[.-1]");
invalid("[.2e-3]");
invalid("[0.1.2]");
invalid("[0.3e+]");
invalid("[0.3e]");
invalid("[0.e1]");
invalid("[0E+]");
invalid("[0E]");
invalid("[0e+]");
invalid("[0e]");
invalid("[1.0e+]");
invalid("[1.0e-]");
invalid("[1.0e]");
invalid("[1 000.0]");
invalid("[1eE2]");
invalid("[2.e+3]");
invalid("[2.e-3]");
invalid("[2.e3]");
invalid("[9.e+]");
invalid("[1+2]");
invalid("[0x1]");
invalid("[0x42]");
invalid("[Inf]");
invalid("[Infinity]");
invalid("[0e+-1]");
invalid("[-123.123foo]");
invalid("[123Â]");
invalid("[1e1Â]");
invalid("[0Â]");
invalid("[-Infinity]");
invalid("[-foo]");
invalid("[- 1]");
invalid("[NaN]");
invalid("[-012]");
invalid("[-.123]");
invalid("[-1x]");
invalid("[1ea]");
invalid("[1eÂ]");
invalid("[1.]");
invalid("[.123]");
invalid("[1]");
invalid("[1.2a-3]");
invalid("[1.8011670033376514H-308]");
invalid("[012]");
invalid("[\"x\", truth]");
invalid("{[: \"x\"}");
invalid("{\"x\", null}");
invalid("{\"x\"::\"b\"}");
invalid("{🇨🇭}");
invalid("{\"a\":\"a\" 123}");
invalid("{key: 'value'}");
invalid("{\"a\" b}");
invalid("{:\"b\"}");
invalid("{\"a\" \"b\"}");
invalid("{\"a\":");
invalid("{\"a\"");
invalid("{1:1}");
invalid("{9999E9999:1}");
invalid("{\"π\":\"0\",}");
invalid("{null:null,null:null}");
invalid("{\"id\":0,,,,,}");
invalid("{'a':0}");
invalid("{\"id\":0,}");
invalid("{\"a\":\"b\"}/**/");
invalid("{\"a\":\"b\"}/**//");
invalid("{\"a\":\"b\"}//");
invalid("{\"a\":\"b\"}/");
invalid("{\"a\":\"b\",,\"c\":\"d\"}");
invalid("{a: \"b\"}");
invalid("{\"a\":\"a");
invalid("{ \"foo\" : \"bar\", \"a\" }");
invalid("{\"a\":\"b\"}#");
invalid(" ");
invalid("[\"\\uD800\\\"]");
invalid("[\"\\uD800\\u\"]");
invalid("[\"\\uD800\\u1\"]");
invalid("[\"\\uD800\\u1x\"]");
invalid("[é]");
invalid("[\"\\\"]");
invalid("[\"\\x00\"]");
invalid("[\"\\\\\\\"]");
invalid("[\"\\ \"]");
invalid("[\"\\🌀\"]");
invalid("[\"\\\"]");
invalid("[\"\\u00A\"]");
invalid("[\"\\uD834\\uDd\"]");
invalid("[\"\\uD800\\uD800\\x\"]");
invalid("[\"\\uÂ\"]");
invalid("[\"\\a\"]");
invalid("[\"\\uqqqq\"]");
invalid("[\"\\Â\"]");
invalid("[\\u0020\"asd\"]");
invalid("[\\n]");
invalid("\"");
invalid("['single quote']");
invalid("abc");
invalid("[\"\\");
invalid("[\"a\fa\"]");
invalid("[\"new\nline\"]");
invalid("[\"\t\"]");
invalid("\"\\UA66D\"");
invalid("\"\"x");
// Too long
invalid("<.>");
invalid("[<null>]");
invalid("[1]x");
invalid("[1]]");
invalid("[\"asd]");
invalid("aå");
invalid("[True]");
invalid("1]");
invalid("{\"x\": true,");
invalid("[][]");
invalid("]");
invalid("Ôª{}");
invalid("Â");
invalid("[");
invalid("");
invalid("[]\0");
invalid("2@");
invalid("{}}");
invalid("{\"\":");
invalid("{\"a\":/*comment*/\"b\"}");
invalid("{\"a\": true} \"x\"");
invalid("['");
invalid("[,");
// Too long
invalid("[{");
invalid("[\"a");
invalid("[\"a\"");
invalid("{");
invalid("{]");
invalid("{,");
invalid("{[");
invalid("{\"a");
invalid("{'a'");
invalid("[\"\\{[\"\\{[\"\\{[\"\\{");
invalid("È");
invalid("*");
invalid("{\"a\":\"b\"}#{}");
invalid("[⁠]");
invalid("[\\u000A\"\"]");
invalid("[1");
invalid("[ false, nul");
invalid("[ true, fals");
invalid("[ false, tru");
invalid("{\"asd\":\"asd\"");
invalid("å");
invalid("Ôªø");
invalid("[]");
invalid("[⁠]");

roundtrip("[[] ]");
roundtrip("[\"\"]");
roundtrip("[]");
roundtrip("[\"a\"]");
roundtrip("[false]");
roundtrip("[null, 1, \"1\", {}]");
roundtrip("[null]");
roundtrip("[1 ]");
roundtrip(" [1]");
roundtrip("[1,null,null,null,2]");
roundtrip("[2] ");
roundtrip("[123e65]");
roundtrip("[0e+1]");
roundtrip("[0e1]");
roundtrip("[ 4]");
roundtrip("[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]");
roundtrip("[20e1]");
roundtrip("[-0]");
roundtrip("[-123]");
roundtrip("[-1]");
roundtrip("[1E22]");
roundtrip("[1E-2]");
roundtrip("[1E+2]");
roundtrip("[123e45]");
roundtrip("[123.456e78]");
roundtrip("[1e-2]");
roundtrip("[1e+2]");
roundtrip("[123]");
roundtrip("[123.456789]");
roundtrip("{\"asd\":\"sdf\", \"dfg\":\"fgh\"}");
roundtrip("{\"asd\":\"sdf\"}");
roundtrip("{\"a\":\"b\",\"a\":\"c\"}");
roundtrip("{\"a\":\"b\",\"a\":\"b\"}");
roundtrip("{}");
roundtrip("{\"\":0}");
roundtrip("{\"foo\\u0000bar\": 42}");
roundtrip("{ \"min\": -1.0e+28, \"max\": 1.0e+28 }");
roundtrip("{\"x\":[{\"id\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}], \"id\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}");
roundtrip("{\"a\":[]}");
roundtrip("{\"title\":\"\\u041f\\u043e\\u043b\\u0442\\u043e\\u0440\\u0430 \\u0417\\u0435\\u043c\\u043b\\u0435\\u043a\\u043e\\u043f\\u0430\" }");
roundtrip("{ \"a\": \"b\" }");
roundtrip("[\"\\u0060\\u012a\\u12AB\"]");
roundtrip("[\"\\uD801\\udc37\"]");
roundtrip("[\"\\ud83d\\ude39\\ud83d\\udc8d\"]");
roundtrip("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]");
roundtrip("[\"\\\\u0000\"]");
roundtrip("[\"\\\"\"]");
roundtrip("[\"a/*b*/c/*d//e\"]");
roundtrip("[\"\\\\a\"]");
roundtrip("[\"\\\\n\"]");
roundtrip("[\"\\u0012\"]");
roundtrip("[\"\\uFFFF\"]");
roundtrip("[\"asd\"]");
roundtrip("[ \"asd\"]");
roundtrip("[\"\\uDBFF\\uDFFF\"]");
roundtrip("[\"new\\u00A0line\"]");
roundtrip("[\"�\"]");
roundtrip("[\"𛿿\"]");
roundtrip("[\"�\"]");
roundtrip("[\"\\u0000\"]");
roundtrip("[\"\\u002c\"]");
roundtrip("[\"π\"]");
roundtrip("[\"asd \"]");
roundtrip("\" \"");
roundtrip("[\"\\uD834\\uDd1e\"]");
roundtrip("[\"\\u0821\"]");
roundtrip("[\"\\u0123\"]");
roundtrip("[\"
\"]");
roundtrip("[\"
\"]");
roundtrip("[\"\\u0061\\u30af\\u30EA\\u30b9\"]");
roundtrip("[\"new\\u000Aline\"]");
roundtrip("[\"�\"]");
roundtrip("[\"\\uA66D\"]");
roundtrip("[\"⍂㈴⍂\"]");
roundtrip("[\"\\u0022\"]");
roundtrip("[\"\\uDBFF\\uDFFE\"]");
roundtrip("[\"\\uD83F\\uDFFE\"]");
roundtrip("[\"\\u200B\"]");
roundtrip("[\"\\u2064\"]");
roundtrip("[\"\\uFDD0\"]");
roundtrip("[\"\\uFFFE\"]");
roundtrip("[\"\\u005C\"]");
roundtrip("[\"€𝄞\"]");
roundtrip("[\"a�a\"]");
roundtrip("42");
roundtrip("-0.1");
roundtrip("\"asd\"");
roundtrip("\"\"");
roundtrip("[\"a\"]");
roundtrip("[true]");
roundtrip(" [] ");

// number
roundtrip("123");
invalid("0.");
invalid("0.x");
invalid("0ex");
invalid("0e-x1");
invalid("0ex1");

// null
roundtrip("null");
invalid("nill");
invalid("nu1l");
invalid("nul1");

// true
roundtrip("true");
invalid("txue");
invalid("trxe");
invalid("trux");

// false
roundtrip("false");
invalid("fxlse");
invalid("faxse");
invalid("falxe");
invalid("falsx");

// string escapes
roundtrip("\"\\u1234\"");
roundtrip("\"\\uabcd\"");
roundtrip("\"\\uCDEF\"");
invalid("\"\\x\"");
invalid("\"\\u123\"");
invalid("\"\\uabcx\"");

Parsing of long string literals with escaped characters is slow

If you try to parse a long string value with a lot of escape sequences, the parser uses String.sub for getting 2-character substring like \" from the long string. Unfortunately, JavaScript implementation of String.sub works the way it converts the WHOLE string to bytes (one byte at a time), gets requested range from the bytes array and converts it back to string.

This necessarily means that if you have 200,000 char JSON string with 10,000 escape sequences, the whole string is traversed 10,000x 🙁

Buffer.add_string(buffer, Scanf.unescaped(String.sub(text, i, 2)));

Solutions I've come up with:

  • Make sure that Js.String.substrAtMost is used when targeting JavaScript
    • Or do you expect the OCaml-way of handling strings? (String.sub working on bytes instead of UTF8 characters)
    • Usage: Js.String.substrAtMost(~from=i, ~length=2, text)
  • Create or use some existing library for UTF8 strings that compiles to Js.String in case of JavaScript.
    • Do you know some? I've just started with Reason/BS/OCaml so I'm not really sure if there is some "industry standard" of handling UTF strings in Ocaml...

Otherwise thank you for making this fantastically ergonomic library, the infix operators are great!

getting json from path in directory

Trying to see if I can use this to dynamically encode lab.json and theme.json files produced by c8r/lab output. Example in that repo here

I get the following error:


/Users/prisc_000/code/BHYV/lab-rex-json/node_modules/bs-platform/lib/js/pervasives.js:14
  throw [
  ^
Failure,-2,Error "unexpected character" at 1:5 ->   

Running this code with rex-json:

[@bs.module] external theme : string = "./theme.json";

/* let theme = ThemeJs.theme; */
let data = {|
    theme
    |};

/* let data = {|
   {
       "fonts": ["-apple-system, BlinkMacSystemFont, sans-serif"],
       "space": [0, 4, 8, 16, 32, 64, 128, 256],
       "fontSizes": [12, 14, 16, 20, 24, 32, 48, 64, 72, 96],
       "colors": {
         "black": "#000",
         "blue": "#07c",
         "lightgray": "#eee"
       },
       "primary": "red"
     }
   |}; */
/* [@bs.module] external theme : Js.Json.t = "./theme.json"; */
let json = Json.parse(data);

Js.log(data);

Js.log(json);

It works logging if i paste the json right in. Am i doing something stupid?

Thank you.

Json.getPath signature

Hi,

I'll begin by saying, amazing library, thank you!

I've looked at the getPath signature and started wondering whether it wouldn't be nicer if it had the following signature: list(string) => option(string) = <fun>;

It would mean less parsing in the library and support for crazy json authors who include . in their keys.

This is all a bit inspired, by how Ramda does it: https://ramdajs.com/docs/#path

Thanks!

Handle Unicode escapes

Strings in JSON can contain escaped Unicode characters (i.e. \uXXXX) and the spec says that it's legal for any character to be escaped (even though technically you only need to escape a few of them).

The Java LSP client library (lsp4j) is particularly aggressive about escaping characters, so it immediately runs into this issue. For example, it sends:

{
  "jsonrpc": "2.0",
  "method": "textDocument/didOpen",
  "params": {
    "textDocument": {
      "uri": "file:///Users/mdb/ops/langs/reason/a-reason-react-tutorial/src/Main.re",
      "languageId": "re",
      "version": 1,
      "text": "ReactDOMRe.renderToElementWithId(\u003cTodoApp /\u003e, \"root\");\n"
    }
  }
}

(which escapes < and >) and this fails thusly:

Scanf.Scan_failure(\"scanf: bad input at char number 35: \\\"illegal escape character 'u'\\\"\")

I hacked the code to turn \u00XX into \xXX just to work around this for now (it would still fail on any non-ASCII escaped Unicode characters). I don't name my variables with UTF-8 smileys. 😁 But a proper decoding of Unicode escapes will be needed at some point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.