Giter VIP home page Giter VIP logo

json-rust's Introduction

json-rust

Parse and serialize JSON with ease.

Changelog - Complete Documentation - Cargo - Repository

Why?

JSON is a very loose format where anything goes - arrays can hold mixed types, object keys can change types between API calls or not include some keys under some conditions. Mapping that to idiomatic Rust structs introduces friction.

This crate intends to avoid that friction.

let parsed = json::parse(r#"

{
    "code": 200,
    "success": true,
    "payload": {
        "features": [
            "awesome",
            "easyAPI",
            "lowLearningCurve"
        ]
    }
}

"#).unwrap();

let instantiated = object!{
    // quotes on keys are optional
    "code": 200,
    success: true,
    payload: {
        features: [
            "awesome",
            "easyAPI",
            "lowLearningCurve"
        ]
    }
};

assert_eq!(parsed, instantiated);

First class citizen

Using macros and indexing, it's easy to work with the data.

let mut data = object!{
    foo: false,
    bar: null,
    answer: 42,
    list: [null, "world", true]
};

// Partial equality is implemented for most raw types:
assert!(data["foo"] == false);

// And it's type aware, `null` and `false` are different values:
assert!(data["bar"] != false);

// But you can use any Rust number types:
assert!(data["answer"] == 42);
assert!(data["answer"] == 42.0);
assert!(data["answer"] == 42isize);

// Access nested structures, arrays and objects:
assert!(data["list"][0].is_null());
assert!(data["list"][1] == "world");
assert!(data["list"][2] == true);

// Error resilient - accessing properties that don't exist yield null:
assert!(data["this"]["does"]["not"]["exist"].is_null());

// Mutate by assigning:
data["list"][0] = "Hello".into();

// Use the `dump` method to serialize the data:
assert_eq!(data.dump(), r#"{"foo":false,"bar":null,"answer":42,"list":["Hello","world",true]}"#);

// Or pretty print it out:
println!("{:#}", data);

Installation

Just add it to your Cargo.toml file:

[dependencies]
json = "*"

Then import it in your main.rs / lib.rs file:

#[macro_use]
extern crate json;

Performance and Conformance

There used to be a statement here saying that performance is not the main goal of this crate. It is definitely one of them now.

While this crate doesn't provide a way to parse JSON to native Rust structs, it does a lot to optimize its performance for DOM parsing, stringifying and manipulation. It does very well in benchmarks, in some cases it can even outperform parsing to structs.

This crate implements the standard according to the RFC 7159 and ECMA-404 documents. For the best interoperability numbers are treated stored as 64bit precision mantissa with 16 bit decimal exponent for floating point representation.

License

This crate is distributed under the terms of both the MIT license and the Apache License (Version 2.0). Choose whichever one works best for you.

See LICENSE-APACHE and LICENSE-MIT for details.

json-rust's People

Contributors

aembke avatar frk1 avatar gyrosofwar avatar imp avatar joxit avatar jrpascucci avatar konstin avatar lpbak avatar maciejhirsz avatar macisamuele avatar matthias-t avatar mominul avatar seeekr avatar syvb avatar tim-nosco avatar yoric avatar zummenix 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

json-rust's Issues

Ability to "pretty print" serialized JSON

I see that json::codegen::Generator can produce pretty print format. However, this seems to be a private interface. And stringify(), which is public, unconditionally initialises Generator::minify to false.

This is to request user controllable "pretty print" serialization formatting.

Panic when parsing long float

json::parse("2.22507385850720113605740979670913197593481954635164564e-308").unwrap();
thread 'main' panicked at 'attempted to add with overflow', /home/david/.cargo/git/checkouts/json-rust-8fc837b9d242b7ce/0.8.5/src/parser.rs:401

This is on the 0.8.5 branch.

`PartialEq` to replace the `is` method

Instead of data["awesome"].is(true) implement PartialEq for all supported types to make data["awesome"] == true possible.

The added benefit is that PartialEq implementations can use destructing to match the types without casting primitives into JsonValue to compare them.

Panic parsing integer

extern crate json;
fn main() {
    json::parse("18446744073709551616").ok();
}
thread 'main' panicked at 'attempted to add with overflow', /home/david/.cargo/registry/src/github.com-1ecc6299db9ec823/json-0.7.4/src/parser.rs:214

Mutable indexing...

I'm thinking about implementing IndexMut next to Index, so things like this can be done:

data["foo"]["bar"] = "baz".into();

The problem here is that mutable indexing would have to modify the object when accessing a field, basically do what the data.with("foo") does. Question here is whether that behavior is something expectable?

On top of that could also abuse one of the assign operator traits, like BitAndAssign to easily put any Into<JsonValue> values on mutable fields. (edit: can't be done, BitAndAssign has to take same type)

Methods to check truthiness.

I'm finding myself wanting to check for truthyness instead of specific values and maybe it could be nice to have an is_truthy and is_falsy method in JsonValue?

It's just to avoid the !json[key].is_null() && json[key].as_bool().unwrap_or(false) boilerplate everywhere.

Or maybe there's already a way to do it and I'm not seeing it? Great library by the way.

It's possible to remove the `iterators` module entirely.

We can define type aliases for Members , MembersMut, Entries, EntriesMut, for example:

pub type Members<'a> = slice::Iter<'a, JsonValue>;

And change methods to return Option:

pub fn members(&self) -> Option<Members>

It's a little inconvenient on a call site, but that way we can eliminate one level of indirection.
@maciejhirsz, If you're ok with this I can implement it.

Consider adding a take_string method to JsonValue.

When parsing JSON data, it can be useful to take certain string values out and place them into variables/structs which will outlive the original data.

It would be nice to have a fn take_string() -> Option<String> method which would return None if the value wasn't a string and return the inner string, replacing it with JsonValue::Null.

Other variants don't need/can't have this as they are Copy.

Fails to parse valid UTF8

extern crate json;
fn main() {
    println!("{}", json::parse(r#""\uD834\uDD1E""#).unwrap().as_str().unwrap());
}

Expected: ๐„ž

Reorganize modules

Currently the lib.rs contains a lot of code that doesn't belong there - in particular various trait implementations for JsonValue. The value module should be made into a folder, and the specific implementations into their own files. In particular implementations of From and PartialEq should find a way to their own files.

Consider DEC64 or similar for number representation

Currently json-rust is using 64 bit floats to represent numbers. While floats are great and widely supported, they are also very problematic to stringify, and not that easy to parse, either.

DEC64 is one of the proposed decimal floating point formats, and is probably the simplest one, as it can be stored on something like i64. It's coefficient can be then extracted by a single bitshift operation, while the exponent simply by casting to i8. Since the exponent also strictly follows the e notation, it is trivial to both read and write it's string representation. The cost of operations on DEC64 is pretty much irrelevant for us, with the exceptions of casting to and from different types. DEC64 is also a proposal of Douglas Crockford, same figure that discovered JSON and effectively made it into a standard we use today.

One non-trivial cost is converting f64 to DEC64, as that requires much of the same magic that writing floats into strings does. It should be possible to repurpose most of Grisu2 logic to make that possible.

map_* methods

Instead of:

let data = object!{ foo => 10 };

let Some(num) = data["foo"].as_f64() {
    data["foo"] = (num + 1).into();
}

assert_eq(data.dump(), r#"{"foo":11}"#);

Do:

let data = object!{ foo => 10 };

data["foo"].map_f64(|num| num + 1);

assert_eq(data.dump(), r#"{"foo":11}"#);

๐Ÿ‘ or ๐Ÿ‘Ž ?

Parsed json data is not in the same order as the original.

Thanks for this crate!
I'm currently writing a code generator, where you can define the API using JSON. I noticed that if a list of elements is used, the resulting parsed output is not in the same order as defined in the original JSON data, which is a bit of a problem. Example:

What I pass to JSON parser:

{
  "struct": {
  "var": { "type": "int", "name": "x" },
  "func": [ { "type": "int", "name": "return_int" } ]
  }
}

What I get after parsing and printing the parsed data to screen:

{"struct":{"func":[{"name":"return_int","type":"int"}],"var":{"name":"x","type":"int"}}}

Is there a way to get the output exactly in the same order as I defined? So far I'm iterating over members() and entries() and I can't see any other way to do it properly.

Thanks!

\x7F should be allowed in input

According to RFC7159:

All Unicode characters may be placed within the quotation marks, except for the characters that must be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).

Based on that language I would expect the following program to succeed:

extern crate json;
fn main() {
    json::parse("\"\x7F\"").unwrap();
}

Instead it fails with UnexpectedCharacter { ch: '\u{7f}', line: 1, column: 2 }.

Printable JsonValue

I would like to suggest adding std::fmt::Display trait to JsonValue to make it printable.
For example "{}" will print json.dump() and "{:#}" - json.pretty()

.dump() and .pretty() will fail (horribly) with escaped characters

let not_good = object!{
    "howdy" => "http://www.google.com",
    "howdy_stop" => "http://www.google.com/\\",
    "howdy_what" => "http://www.google.com/\\t"
};
println!("{}\n", not_good.pretty(2));
println!("{:#?}", not_good);

yields

{
  "howdy": "http://www.google.com",
  "howdy_stop": "\\",
  "howdy_what": "\\t"
}

Object(
    {
        "howdy": String(
            "http://www.google.com"
        ),
        "howdy_stop": String(
            "http://www.google.com/\\"
        ),
        "howdy_what": String(
            "http://www.google.com/\\t"
        )
    }
)

Should be self explanatory what we'd expect.

Update existing json structure

Hi,
I'm using your code for my settings system. The idea with the system is to have a base set of settings and allow the user to override the default settings with own settings in a separate JSON file that can be (re)loaded.

I wonder if that is possible to do with the current code in some easy way? (I don't expect you to add something in the library to do it but I'm fine with having my own code as long as I can update the data structures)

Iterators

Add ways to iterate over arrays and objects without destructing them.

Deprecation warning/error with json-rust 0.9.0

Thanks for pushing the requests so quickly! :) However, when trying to compile a new cargo project with json 0.9.0, 2 deprecation warnings are being generated:

-*- mode: compilation; default-directory: "d:/hello_world/" -*-
Compilation started at Sat Jul 16 20:01:46

make -k 
RUST_BACKTRACE=1 cargo run
   Compiling bitflags v0.7.0
   Compiling ftoa v0.1.1
   Compiling itoa v0.1.1
   Compiling xml-rs v0.3.4
   Compiling json v0.9.0
C:\Users\(...)\json-0.9.0\src\lib.rs:238:1: 238:63 error: `#[deprecated]` attribute is unstable (see issue #29935)
C:\Users\(...)\json-0.9.0\src\lib.rs:238 #[deprecated(since="0.9.0", note="use `json::Error` instead")]
                                                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\(...)\json-0.9.0\src\lib.rs:241:1: 241:64 error: `#[deprecated]` attribute is unstable (see issue #29935)
C:\Users\(...)\json-0.9.0\src\lib.rs:241 #[deprecated(since="0.9.0", note="use `json::Result` instead")]
                                                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
Build failed, waiting for other jobs to finish...
Could not compile `json`.

Since it's set to treat deprecations as errors, maybe just remove them altogether?

Improve README, add a GitBook?

I think the current the examples in README are trying to explain too many things at once. The first example in particular is pretty odd. I need to rework it with some very very simple examples, maybe just one, showing how parsing, checking a value, mutating and stringifying works.

On top of that, a GitBook talking about how to use the library might be a nice addition.

Using Strings as index

Right now it's kind of verbose to do this:

let j_test = object!{
    "hi" => 123,
    "na" => 345
};
let mut j_test2 = json::JsonValue::new_object();
for (key, val) in j_test.entries() {
    j_test2["subsub"][key] = val.as_u32().into();
}

Error:

trait bound `json::JsonValue: std::ops::Index<&std::string::String>` is not satisfied

You have to write

j_test2["subsub"][&**key] = val.as_u32().into();

And if you use ref you have to write:

for (ref key, ref val) in j_test.entries() {
    j_test2["subsub"][&***key] = val.as_u32().into();
}

As an idea for improvements.

Move tests from `tests/lib.rs` to separate files.

I noticed that tests/lib.rs contains several modules.

  • Internals of unit can be moved to tests/unit.rs
  • Internals of json_checker_fail can be moved to tests/checker_fail.rs
  • Internals of json_checker_pass can be moved to tests/checker_pass.rs

slashes in the .dump() and .pretty() output are escaped

output generated by .dump() and .pretty() has all the slashes ("/") symbols escaped with backslash ("") - like that -

{
    "path": "\/etc\/motd"
}

That doesn't seem to be pretty [as name may imply :)]. Probably an artifact of the internal conversion. I think the backslashes should be dropped.

More context in error messages

Hi,

Thanks for a great crate!

My plan is to use it for user configuration and one issue I have is that if a user makes an error in the configuration I would like to report back a meaningful error. In this case for example an extra , has been added on bar

"some_data":{
    "foo": 0,
    "bar": 42,
},

The error I get back is UnexpectedToken("BraceOff") which is correct but I have no idea of where the error occurs. I'm not sure if the code keeps track of line numbers but it would be very helpful if it could report on which line the error happen so I could report that back to the user.

Serialize numbers < 1e-15 or >= 1e+21 to scientific notation

Currently codegen prints out plain integers and floats, but for large numbers using a scientific notation is desirable.

JS stringifies integers up to 1e+21 (exclusive). The floating precision varies, but going after the default value of Math.PI I'd assume 1e-15 being the boundary should be fine.

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.