Giter Club home page Giter Club logo

yyjson's People

Contributors

0ihsan avatar alex-tee avatar antonshalgachev avatar carterli avatar chad-earthscope avatar cheney-w avatar cntrump avatar faultaddr avatar fd00 avatar hxdnshx avatar ibireme avatar islc avatar liuxiang88 avatar liuzicheng1987 avatar lshamis avatar lundmark avatar maxice8 avatar myd7349 avatar nixzhu avatar parkertomatoes avatar pavelxdd avatar tktech avatar unbrand 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  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

yyjson's Issues

Add context to pointer results (yyjson_mut_get_pointern_ex)

I find it common to want to replace the value discovered by a call to yyjson_mut_get_pointer, however the current API provides no context about where in the document the match was found.

I propose a yyjson_mut_get_pointern_ex (which yyjson_mut_get_pointer would use) with something like:

yyjson_mut_val * yyjson_mut_get_pointern_ex(
    const char *pointer,
    size_t pointer_len,
    yyjson_mut_val *container,
    const char *key,
    size_t *idx;
)

This would return the pointer as it does now, but would also populate container (if not NULL) with the yyjson_mut_val that contains the matching value. If container is a yyjson_mut_obj, it would populate key with the matching key. If container is a yyjson_mut_arr, it would populate idx. This would provide enough information to replace the returned value in its parent.

I'm happy to implement this one, but I'm hoping you have an idea for a better API.

Convert yyjson_val to yyjson_mut_val

I want to edit a yyjson_val object. So I want to convert it to yyjson_mut_val edit it and then convert it back to yyjson_val.

What's the ideal way to do this?

[Feature Request] Max possible pool size given json length

Given the length of a json string, what is the upper bound on the size of a pool alc that could handle that json?

    size_t pool_size;
    yyjson_alc_pool_max_size(json_size, &pool_size);

    void *buf = alloca(pool_size)
    yyjson_alc alc;
    yyjson_alc_pool_init(&alc, buf, pool_size);

Documentation for arrays of objects

Hi!

Your library is great! But one thing I couldn't find is how to serialize and deserialize arrays of objects, as well as nested objects. I saw that you have a couple of closed tickets regarding how to work with arrays of objects, so I believe that explaining it in the documentation will help a lot.

My main use for this library would be to dump arrays of C structs (and sometimes a struct can have another array of structs nested inside it)... so documentation about this would be very welcome!

Questions about the implemenation of f64_bin_to_dec

yyjson/src/yyjson.c

Lines 5712 to 5717 in 19220b1

/* exp_bin: [-1074, 971] */
/* k = lower_bound_closer ? floor(log10(pow(2, exp_bin))) */
/* : floor(log10(pow(2, exp_bin) * 3.0 / 4.0)) */
/* = lower_bound_closer ? floor(exp_bin * log10(2)) */
/* : floor(exp_bin * log10(2) + log10(3.0 / 4.0)) */
k = (i32)(exp_bin * 315653 - (lower_bound_closer ? 131237 : 0)) >> 20;

  1. The code is inconsistent to the comment. Which is the lower_bound_closer case? Refer to the shorter interval case in the Dragonbox paper, I believe the code is more correct than the comment.

  2. How does 131237 come? It seems not the best magic, although already works for [-1074, 971] with u = 20. (When u = 20, even the best one may fail with e = -1335.)

yyjson_mut_write_val & yyjson_write_val

It's fairly typical to want to extract just part of a document, then write it out. However, there's no current way to print a subset of a document without getting the value, making it the new root, and then writing the entire document.

Ex: Writing the result of a json_merge_patch(), or writing the result of a yyjson_mut_get_pointer().

vc6.0 mfc dll build error

vc6.0 mfc project
The dll module introduces yyjson and creates a method of write string
.exe module uses methods in the dll module
Dlls compile alone without problems, .exe compilation prompts
JsonTest.obj : error LNK2001: unresolved external symbol _yyjson_mut_doc_free
JsonTest.obj : error LNK2001: unresolved external symbol _yyjson_mut_doc_new
JsonTest.obj : error LNK2001: unresolved external symbol _yyjson_mut_write_opts
JsonTest.obj : error LNK2001: unresolved external symbol _unsafe_yyjson_val_pool_grow
Debug/JsonTest.exe : fatal error LNK1120: 4 unresolved externals

What should I do?

Document the memory behaviour of mutable values.

In py_yyjson, I've added JSON Pointer support which includes the ability to patch the value at the pointer with another yyjson_mut_val. However, I noticed that removal doesn't automatically free a value, and there appears to be no explicit API to free a value.

This seems to be by design - the pool for a mutable doc can only be created, grown, or freed in its entirety. There's nothing wrong with this approach, but it should be documented that values, once created, cannot be destroyed. Thus yyjson_mut_doc is more suitable for write-once, than mutation of an existing document.

yyjson.h: need to properly compile with -Wundef compiler option

Read JSON with options: Allow INF and NAN flag fails to read positive infinity values

Description
When enabling the YYJSON_READ_ALLOW_INF_AND_NAN flag, the JSON read fails, returning an unexpected character error with code 6.

  • This happens with both array and object values.
  • This doesn't happen with negative infinity as those values are caught by the char_is_number(*cur) check, and handled accordingly.
  • Possible fix below.

Additional context

Running the code below results in these two errors:

  • Array JSON read error: unexpected character. Code: 6, at position: 28
  • Object JSON read error: unexpected character. Code: 6, at position: 78

How to reproduce

yyjson_read_flag flg = YYJSON_READ_ALLOW_INF_AND_NAN;

//
// Read array with nan and inf values
//
char arr_str[] = "[1, 123e999, nan, NaN, NAN, inf, infinity, Infinity, INF, -inf, -infinity, -Infinity, -INF]";
size_t arr_str_len = strlen(arr_str);
yyjson_read_err arr_err;

yyjson_doc *arr_doc = yyjson_read_opts(arr_str, arr_str_len, flg, NULL, &arr_err);
if (!arr_doc) {
    fprintf(stderr, "Array JSON read error: %s. Code: %u, at position: %ld\n", arr_err.msg, arr_err.code, arr_err.pos);
}
yyjson_doc_free(arr_doc);

//
// Read object with nan and inf values
//
char obj_str[] = "{\"small\": 1, \"large\": 123e999, \"nan1\": nan, \"nan2\": NaN, \"nan3\": NAN, \"inf1\": inf, \"inf2\": infinity, \"inf3\": Infinity, \"inf4\": INF, \"inf5\": -inf, \"inf6\": -infinity, \"inf7\": -Infinity, \"inf8\": -INF}";
size_t obj_str_len = strlen(obj_str);
yyjson_read_err obj_err;

yyjson_doc *obj_doc = yyjson_read_opts(obj_str, obj_str_len, flg, NULL, &obj_err);
if (!obj_doc) {
    fprintf(stderr, "Object JSON read error: %s. Code: %u, at position: %ld\n", obj_err.msg, obj_err.code, obj_err.pos);
}
yyjson_doc_free(obj_doc);

Possible fix
Replace all instances of:
if (has_flag(ALLOW_INF_AND_NAN) && *cur == 'N')
with:
if (has_flag(ALLOW_INF_AND_NAN) && *cur == 'i' || *cur == 'I' || *cur == 'N')

How to parse complex string by yyjson

Hello,my input string structure is like this:

[{"addition":{"barcode":{"barcode":"78209172869467","position":[{"x":"1509","y":"1797"},{"x":"1213","y":"1518"},{"x":"1179","y":"1555"},{"x":"1475","y":"1833"}]},"threecode":["341;7-13;002","341;7-13;004"...]},"extend":"","filename":"/home/linaro/78209172869467.jpg","image":{"colorspace":"RGB","encodeData":"/9j/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/wAARCAvcD7gDASIAAhEBAxEB/9oADAMBAAIRAxEAPwD49HWg9KDRj1oAB69KXj60YpO9AAelL3pDz9aKACl70DHNIfXHNAC4xSduKU84pB3FACjqPejNJ6Gl45oAWkFHUUDr60AHaj8aT1FL2oAPWjPFAo7UAL3NAAK+lNpV70AHeg9KD2p3G09c0ANPSgcUp6e9J1OKAEpW4IOaDRxgUAKOh5pD1FKO4pD9aAF74pB0FHf8KXsaADvSHuKO4NKw560AJ2BpV69KQA4oHWgAPalI5pD0pc9KADsRSdxSjuOhpD0oAD1NA6ClIAPNIBwaAAdaSl6EdqPUUALSd6UdKP4qADqKMYOBR60fWgBV5BpD1BoHUikPIoAX1oHQUHrQOlAB1ag9KO9LjrQAnpRjGaB0FH8VAB2BpR3pO1KOooAQfdo70oHUdM0dgaAE9RR6Up+8cUfwmgA6tikwcc0vejHWgA46igdxR2Bo/iNACenpTj1pp6daUdeaAAfdoHWgYAIoPbFAAR1oPOKG6mkHSgBV6mjsMcUDqKD3FACnqKQc5o9OvNB6mgAx0JpzfePpTe2aXvQAnb6Uv8VC9DSDqKADpkUvpQOpo/gH1oAQcEilwMZ70d+vakPQjFADj..."}}]

anyone knows how to parse this kind of input using yyjson,Thx a lot! the grammar was not easy for newer.

Returning NULL in a correct Json

Describe the bug
I have this Json that comes from the Discord API: {\"url\": \"wss://gateway.discord.gg\", \"shards\": 1, \"session_start_limit\": {\"total\": 1000, \"remaining\": 986, \"reset_after\": 1501475, \"max_concurrency\": 1}}\r\n0\r\n\r\n and i do this to try to read it:

yyjson_doc *json_doc = yyjson_read(json, strlen(json), 0);
yyjson_val *root = yyjson_doc_get_root(json_doc);

yyjson_val *url = yyjson_obj_get(root, "url");

But the json_doc pointer always returns NULL, and I don't know what it could be, because I've already checked Json's formatting on a website and it's all right, I've already checked the string and it's all right, I don't know what could be happening anymore

Your environment

  • OS: Windows 10 2004 - 64 Bits
  • Compiler: MSVC (Visual Studio 2019)

Additional context
I installed the library by vcpkg

stack-buffer-overflow for strings that are not null-terminated

Describe the bug
With this example program:

#include "yyjson.h"

int main(int argc, char const *argv[]) {
	// Create a non-delimited string
	char my_string[20];
	for (int i = 0; i < 20; i++) {
		my_string[i] = 'a';
	}
	// Create a doc with this string as a key/value pair
	yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
	yyjson_mut_val *obj = yyjson_mut_obj(doc);
	yyjson_mut_val *val = yyjson_mut_strn(doc, my_string, 20);
	yyjson_mut_obj_add(obj, val, val);
	yyjson_mut_doc_set_root(doc, obj);

	// Write the doc
	size_t len;
	char *json = yyjson_mut_write(doc, 0, &len);
	return 0;
}

Compiled with cc -fsanitize=address -o my_string_test my_string_test.c yyjson.c
I ran ./my_string_test, which should finish normally.
Instead we get undefined behaviour AddressSanitizer: stack-buffer-overflow ... and so on.

This can be fixed by adding

	if (end - str < 16)
		goto copy_next;

to yyjson.c:6380. You may have a better idea about how to fix this more efficiently though, so I decided not to send a PR :)

Your environment

  • OS: macOS
  • Compiler: Apple clang version 13.0.0

Additional context
I am using yyjson to create a JSON extension for DuckDB. Your library is amazing! Many thanks.

Renaming a key

What's the best way to rename a key in a json object?

Write yyjson issue

Hi ibireme
Thank you very much for your last reply, but I found a problem, please help me to solve it, and I look forward to your reply

//How to find yyjson_mut_val* according to key
yyjson_mut_val* getOrCreateChildMutObj(yyjson_mut_doc doc, yyjson_mut_val parent, const std::string &key)
{
yyjson_mut_val* child = yyjson_mut_get_pointer(parent, key.data());
if (!child)// if not find, build key/value
{
child = yyjson_mut_obj(doc);
yyjson_mut_obj_add_val(doc, parent, key.data(), child);
}
return child;
}
//How to find array nodes(yyjson_mut_val*) based on key ?
yyjson_mut_val* getOrCreateArray(yyjson_mut_doc *doc, yyjson_mut_val arryParent, const std::string &key)
{
yyjson_mut_val arry = yyjson_mut_get_pointer(arryParent, key.data());
if (!arry)
{
arry = yyjson_mut_arr(doc);
yyjson_mut_obj_add_val(doc, arryParent, key.data(), arry);
}
return arry;
}

void funcLayout(yyjson_mut_doc *doc)
{
std::string key1("PATH_KEY_NODE");
std::string key2("INFO_KEY_NODE");

//case 1
{
// auto val = getOrCreateChildMutObj(doc, doc->root, key1);
// yyjson_mut_val* layoutKeyNode = getOrCreateChildMutObj(doc, doc->root, key2);
//output: It seems something went wrong
/*
{
"???"m�\u0000\u0000?K\u0001\u0000": {},
"INFO_KEY?????�": {}
}
*/
}

//case 2
{
yyjson_mut_val* child = yyjson_mut_get_pointer(doc->root, key1.data());
if (!child)
{
child = yyjson_mut_obj(doc);
yyjson_mut_obj_add_val(doc, doc->root, key1.data(), child);
}

yyjson_mut_val* child1 = yyjson_mut_get_pointer(doc->root, key2.data());
if (!child1)
{
    child1 = yyjson_mut_obj(doc);
    yyjson_mut_obj_add_val(doc, doc->root, key2.data(), child1);
}

//output: Should be correct
/*
    {
        "PATH_KEY_NODE": {},
        "INFO_KEY_NODE": {}
    }
*/

}

}

void writeJson()
{
yyjson_mut_doc *doc = yyjson_mut_doc_new(nullptr);
yyjson_mut_doc_set_root(doc, yyjson_mut_obj(doc));

funcLayout(doc);
//funcVerify(doc);

yyjson_write_flag flg = YYJSON_WRITE_PRETTY /| YYJSON_WRITE_ESCAPE_UNICODE/;
yyjson_write_err err;
yyjson_mut_write_file("config.json", doc, flg, nullptr, &err);

yyjson_mut_doc_free(doc);

}

////////////////
Use yyjson in qt :
1、The result of this use is wrong :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

writeJson();  //////////////////////////

return a.exec();

}

2、The result of calling at the very beginning of the program is correct
int main(int argc, char *argv[])
{

 writeJson(); //The result of calling at the very beginning of the program is correct 

QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();

}

//////////////////////////////////
The output result of my case 2 method should be correct, but the output result of case 1 seems to be wrong. Can't you use function calls? There is still a problem with the method I use. Looking forward to your reply, thank you

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string key1("LAYOUT_PATH_KEY_NODE");
std::string key2("VERIFY_KEY_NODE");
std::string input("input_layout");
std::string output("output_layout");
std::string outlevel("output_level");

//case 1
{
yyjson_mut_val* child1 = yyjson_mut_obj(doc);
yyjson_mut_obj_add_val(doc, doc->root, "ROOT", child1);
getOrCreateChildMutObj(doc, child1, "key2");
getOrCreateChildMutObj(doc, child1, "key23");

yyjson_mut_val* layoutKeyNode = getOrCreateChildMutObj(doc, child1, key1);
yyjson_mut_val* arryNode = getOrCreateArray(doc, layoutKeyNode, outlevel);

 //Is there a problem with inserting data like this???
std::vector<std::string> v{"kkkkk33333", "wwwwwwwwww", "rrrrrrrrrr", "qqqqqqqqqqqqq"};
for(size_t i = 0; i < v.size(); ++i)
    yyjson_mut_arr_add_strcpy(doc, arryNode, v[i].data());

 #This way of access is correct, but how do I want to dynamically insert data? 
//         yyjson_mut_arr_add_strcpy(doc, arryNode, "yyyyyyy");

// yyjson_mut_arr_add_strcpy(doc, arryNode, "out/debug/bin/config.json");
// yyjson_mut_arr_add_strcpy(doc, arryNode, "out/debug/bin/config2.json");
// yyjson_mut_arr_add_strcpy(doc, arryNode, "ttxttttt");

}

output:
"ROOT": {
"key2": {},
"key23": {},
"LAYOUT_PATH_KEY_NODE": {
"�\u0000\u0000\u0000\u0000\u0000\u0000\u0000�\u0000\u0000\u0000": [
"kkkkk33333",
"wwwwwwwwww",
"rrrrrrrrrr",
"qqqqqqqqqqqqq"
]
}
}

Hi ibireme
how do I want to dynamically insert data?

for example :
std::vectorstd::string v{"kkkkk33333", "wwwwwwwwww", "rrrrrrrrrr", "qqqqqqqqqqqqq"};
yyjson_mut_val* arryNode; //obj or arry
for(size_t i = 0; i < v.size(); ++i)
yyjson_mut_arr_add_strcpy(doc, arryNode, v[i].data());

The file name suffix is .cpp, then compile with g++ ,Can I use g++ to compile?

///////////////////////////////////////////

cppcheck analyzer issues

There are some issues I found when checking yyjson with cppcheck.

pre_key = cur_key;

The assignment of pre_key is useless, since it's never used.

yyjson/src/yyjson.c

Lines 4129 to 4130 in 94e47ab

goto skip_ascii_begin;
repeat16_incr(expr_stop);

yyjson/src/yyjson.c

Lines 6560 to 6562 in 94e47ab

return cur;
repeat16_incr(expr_stop);

Expressions after goto or return are unreachable. repeat16_incr will never be executed.

if (file_size < 0 || (file_size + 1) < 0) file_size = 0;

(file_size + 1) < 0 is always false. If you wanted to check for long overflow then this is undefined bahaviour (signed integer overflow is UB).

if (!has_flag(INSITU) && hdr) alc.free(alc.ctx, (void *)hdr);

hdr is always not NULL, since it's checked after malloc.

Hello, is your body well?

Since I saw your illness on your blog, there has been no news about you. I am very happy to see your newly created Repo. All recovered now ?

write yyjson to file issue

Hi ibireme
Thank you very much for your last reply, but I found a problem, please help me to solve it, and I look forward to your reply

//How to find yyjson_mut_val* according to key
yyjson_mut_val* getOrCreateChildMutObj(yyjson_mut_doc doc, yyjson_mut_val parent, const std::string &key)
{
yyjson_mut_val* child = yyjson_mut_get_pointer(parent, key.data());
if (!child)// if not find, build key/value
{
child = yyjson_mut_obj(doc);
yyjson_mut_obj_add_val(doc, parent, key.data(), child);
}
return child;
}
//How to find array nodes(yyjson_mut_val*) based on key ?
yyjson_mut_val* getOrCreateArray(yyjson_mut_doc *doc, yyjson_mut_val arryParent, const std::string &key)
{
yyjson_mut_val arry = yyjson_mut_get_pointer(arryParent, key.data());
if (!arry)
{
arry = yyjson_mut_arr(doc);
yyjson_mut_obj_add_val(doc, arryParent, key.data(), arry);
}
return arry;
}

void funcLayout(yyjson_mut_doc *doc)
{
std::string key1("PATH_KEY_NODE");
std::string key2("INFO_KEY_NODE");

//case 1
{
    // auto val = getOrCreateChildMutObj(doc, doc->root, key1);
    //  yyjson_mut_val* layoutKeyNode = getOrCreateChildMutObj(doc, doc->root, key2);
    //output: It seems something went wrong
    /*
       {
        "???\"m�\u0000\u0000?K\u0001\u0000": {},
        "INFO_KEY?????�": {}
       }
    */
}

//case 2
{
    yyjson_mut_val* child = yyjson_mut_get_pointer(doc->root, key1.data());
    if (!child)
    {
        child = yyjson_mut_obj(doc);
        yyjson_mut_obj_add_val(doc, doc->root, key1.data(), child);
    }

    yyjson_mut_val* child1 = yyjson_mut_get_pointer(doc->root, key2.data());
    if (!child1)
    {
        child1 = yyjson_mut_obj(doc);
        yyjson_mut_obj_add_val(doc, doc->root, key2.data(), child1);
    }

    //output: Should be correct
    /*
        {
            "PATH_KEY_NODE": {},
            "INFO_KEY_NODE": {}
        }
    */
}

}

void writeJson()
{
yyjson_mut_doc *doc = yyjson_mut_doc_new(nullptr);
yyjson_mut_doc_set_root(doc, yyjson_mut_obj(doc));

funcLayout(doc);
//funcVerify(doc);

yyjson_write_flag flg = YYJSON_WRITE_PRETTY /*| YYJSON_WRITE_ESCAPE_UNICODE*/;
yyjson_write_err err;
yyjson_mut_write_file("config.json", doc, flg, nullptr, &err);

yyjson_mut_doc_free(doc);

}

////////
1、The result of calling at the very beginning of the program is correct 
int main(int argc, char *argv[])
{

     writeJson();  //The result of calling at the very beginning of the program is correct 

    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

2、The result of this call is wrong 
int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    writeJson(); ///////////

    return a.exec();
}

//////////////////////////////////
The output result of my case 2 method should be correct, but the output result of case 1 seems to be wrong. Can't you use function calls? There is still a problem with the method I use. Looking forward to your reply, thank you

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string key1("LAYOUT_PATH_KEY_NODE");
std::string key2("VERIFY_KEY_NODE");
std::string input("input_layout");
std::string output("output_layout");
std::string outlevel("output_level");

//case 1
{
    yyjson_mut_val* child1 = yyjson_mut_obj(doc);
    yyjson_mut_obj_add_val(doc, doc->root, "ROOT", child1);
    getOrCreateChildMutObj(doc, child1, "key2");
    getOrCreateChildMutObj(doc, child1, "key23");


    yyjson_mut_val* layoutKeyNode = getOrCreateChildMutObj(doc, child1, key1);
    yyjson_mut_val* arryNode = getOrCreateArray(doc, layoutKeyNode, outlevel);

     //Is there a problem with inserting data like this???
    std::vector<std::string> v{"kkkkk33333", "wwwwwwwwww", "rrrrrrrrrr", "qqqqqqqqqqqqq"};
    for(size_t i = 0; i < v.size(); ++i)
        yyjson_mut_arr_add_strcpy(doc, arryNode, v[i].data());

     #This way of access is correct, but how do I want to dynamically insert data? 
    //         yyjson_mut_arr_add_strcpy(doc, arryNode, "yyyyyyy");
  //         yyjson_mut_arr_add_strcpy(doc, arryNode, "out/debug/bin/config.json");
  //         yyjson_mut_arr_add_strcpy(doc, arryNode, "out/debug/bin/config2.json");
  //         yyjson_mut_arr_add_strcpy(doc, arryNode, "ttxttttt");

}

output:
"ROOT": {
"key2": {},
"key23": {},
"LAYOUT_PATH_KEY_NODE": {
"�\u0000\u0000\u0000\u0000\u0000\u0000\u0000�\u0000\u0000\u0000": [
"kkkkk33333",
"wwwwwwwwww",
"rrrrrrrrrr",
"qqqqqqqqqqqqq"
]
}
}

Hi ibireme
how do I want to dynamically insert data?

for example :
std::vectorstd::string v{"kkkkk33333", "wwwwwwwwww", "rrrrrrrrrr", "qqqqqqqqqqqqq"};
yyjson_mut_val* arryNode; //obj or arry
for(size_t i = 0; i < v.size(); ++i)
yyjson_mut_arr_add_strcpy(doc, arryNode, v[i].data());

The file name suffix is .cpp, then compile with g++ ,Can I use g++ to compile?

Cannot read specific JSON input when compiled for 32-bit

Describe the bug
When compiled with flag -m32 reading the following JSON returns NULL. Reading is fine when compiling for 64-bit.

{
    "firstName": "John",
    "lastName": "Smith",
    "isAlive": true,
    "age": 25,
    "address": {
      "streetAddress": "21 2nd Street",
      "city": "New York",
      "state": "NY",
      "postalCode": "10021-3100"
    },
    "phoneNumbers": [
      {
        "type": "home",
        "number": "212 555-1234"
      },
      {
        "type": "office",
        "number": "646 555-4567"
      }
    ],
    "children": [],
    "spouse": null
    }

Your environment

  • OS: Ubuntu 20.04 x64
  • Compiler: gcc 5.3.1

Additional context
DuckDB uses yyjson for its JSON extension. We had no issues with this specific JSON string before (it is part of our tests). I updated yyjson to the latest version, and this issue occurred.

How can I get the key in yyjson_mut_val ?

Is your feature request related to a problem? Please describe.
I can't find how to get the key of the yyjson_mut_val,is there any functions to get the key ?

Describe the solution you'd like
can sombody add get key functions to yyjson lib?

Describe alternatives you've considered
as we known,json is key/value based protocol, Get the key name is an very important function to json libs.

Additional context
Add any other context or screenshots about the feature request here.

Public API to allow building values without allocation (no mut_doc APIs)

Is your feature request related to a problem? Please describe.
It would be nice if we could have public APIs to initialize yyjson_mut_val stack objects that we could then pass along to yyjson_mut_obj_add / yyjson_mut_arr_append. That's because most of the time, the responses are simple and static enough for this to work.

Describe the solution you'd like
Say for instance I want to build:

{code: 5, params: [1, "error-msg"]};

it would be nice if I could do it with no allocations

yyjson_mut_val root, code_key, code_val, params_key, params_arr, one, msg;

/*
 * start of missing API
 */
 yyjson_mut_set_obj(&root);
 yyjson_mut_set_str(&code_key, "code_key");
 yyjson_mut_set_int(&code_val, 5);
 yyjson_mut_set_str(&params_key, "params");
 yyjson_mut_set_arr(&params_arr);
 yyjson_mut_set_int(&one, 1);
 yyjson_mut_set_str(&msg, "error-msg");
 /*
  * end of missing APIs
  */

 // we can already do those
 yyjson_mut_obj_add(&root, &code_key, &code_val);
 yyjson_mut_obj_add(&root, &params_key, &params);
 yyjson_mut_arr_append(&params, &one);
 yyjson_mut_arr_append(&params, &msg);
 
 char buf[100];
 // ....
 yyjson_mut_val_write_opts(&root, &alc ....);

Describe alternatives you've considered
I guess tags could be set manually on the struct instances, but tags are advertised as being part of the private API.

JSON Arrays Object Arrays parse error

Describe the bug
The first array of objects with unrecognized infos

Your environment

  • OS: Windows 10
  • Compiler: MINGW, TCC

Additional context
json

{
	"infos": [
		{
			"names": [
				"a",
				"b",
				"c"
			]
		},
		{
			"names": [
				"d",
				"e",
				"f",
				"g"
			]
		}
	]
}

code

yyjson_read_flag flag = YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS;

    yyjson_read_err err;

    yyjson_doc *doc = yyjson_read_file("data.json", flag, NULL, &err);

    if (doc) {
        yyjson_val *root = yyjson_doc_get_root(doc);
        yyjson_val *infos = yyjson_obj_get(root, "infos");

        if (yyjson_is_arr(infos)) {

            printf("infos.size=%zu\n", yyjson_arr_size(infos));

            yyjson_val *val1;
            yyjson_arr_iter iter1;
            yyjson_arr_iter_init(infos, &iter1);

            while ((val1 = yyjson_arr_iter_next(&iter1))) {

                if (yyjson_is_obj(val1)) {
                    yyjson_val *names = yyjson_obj_get(val1, "names");

                    if (yyjson_is_arr(names)) {
                        yyjson_val *val2;
                        yyjson_arr_iter iter2;
                        yyjson_arr_iter_init(names, &iter2);

                        while ((val2 = yyjson_arr_iter_next(&iter2))) {
                            printf("%s\n", yyjson_get_str(val2));
                        }
                    } else {
                        printf("type=%d\n", yyjson_get_type(names));
                    }
                }

            }

        }

    } else {
        printf("read error (%u): %s at position: %zu\n", err.code, err.msg, err.pos);
    }

    yyjson_doc_free(doc);

result

infos.size=2
type=0
d
e
f
g

desired result

infos.size=2
a
b
c
d
e
f
g

SAX-like parsing and writing

Is your feature request related to a problem? Please describe.
The library requires loading into its own data structure when reading and building a data structure when writing.
This incurs some overhead if one wants to just load or save a custom data structure.

Describe the solution you'd like
It would be good to have a direct interface to the low-level parser, maybe in terms of callbacks for reading. For writing, just exposing a simple API to format the various JSON elements might be good.

Describe alternatives you've considered
Right now, one parses the data into the yyjson document and then reads from it.

How to add object to another object?

{
  "0":{
      "one":"text"
   }
}

and add to it

{
  "1":{
      "one":"text"
   }
}

to become

{
  "0":{
      "one":"text"
   }
  "1":{
      "one":"text"
   }
}

Great lib with bad doc =(

Some fields are written to json, but there are garbled characters in the fields that generate json.

Describe the bug
I am working for the project, it needs some fields are wittern to json, but there are garbled characters in the fields that generate json.

Your environment

  • OS: CentOS Linux release 7.5.1804 (Core)
  • Compiler: gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC)

Additional context
here is the generate json, please look at the PersonAppearTime field, I don't know what's wrong is it?
//=================================================================================
"URI": "/VIID/Person",
"Method": "POST",
"X-Statistics": "TraceId=7f27da55bb6e4031bd9078e96494dab9;Camera=34040000111310100055,20220914171604000;CPSS=000905123456785,20220914171604656,20220914171604855,90;",
"Content": {
"PersonListObject": {
"PersonObject": [
{
"PersonID": "22020422999999999999999999999999999999999999999",
"DeviceID": "34040000111310100055",
"InfoKind": 3,
"SourceID": "22020422001321001366022020091514153701019",
"LeftTopX": 1663,
"LeftTopY": 653,
"RightBtmX": 1733,
"RightBtmY": 753,
"LocationMarkTime": "20220914171604000",
"PersonAppearTime": "\u0005\u0005\u0000\u0000\u0000\u0000\u0000\u0000�h�\u0000\u0000\u0000\u0000\u0000�",
"PersonDisAppearTime": "20220914171604000",
"IDType": "990",
"IDNumber": "19",
//=================================================================================

the code:
yyjson_mut_doc pdoc = yyjson_mut_doc_new(NULL);
vector<string
> vtBufs;
yyjson_mut_val *root = yyjson_mut_obj(pdoc);

	yyjson_mut_doc_set_root(pdoc,root);

	//MessageID
	yyjson_mut_obj_add_str(pdoc,root,"MessageID",struuid.c_str());

	//SourceDeviceID
	yyjson_mut_obj_add_str(pdoc,root,"SourceDeviceID",CpssConfig::GetInstance()->m_sourceDeviceID.c_str());

	//Uri
	yyjson_mut_obj_add_str(pdoc,root,"URI","/VIID/Faces");

	//Method 
	yyjson_mut_obj_add_str(pdoc,root,"Method","POST");

            //........................


	//LocationMarkTime
	if (m_UsedPerson->LocationMarkTime.bUsed)
	{			yyjson_mut_obj_add_str(pdoc,pPersonData,"LocationMarkTime",CFuncLib::TimeToDataTime(m_LocationMarkTime).c_str());
	}	
	//PersonAppearTime
	if (m_UsedPerson->PersonAppearTime.bUsed)
	{			yyjson_mut_obj_add_str(pdoc,pPersonData,"PersonAppearTime",CFuncLib::TimeToDataTime(m_PersonAppearTime).c_str());
	}	

	//PersonDisAppearTime
	if (m_UsedPerson->PersonDisAppearTime.bUsed)
	{			yyjson_mut_obj_add_str(pdoc,pPersonData,"PersonDisAppearTime",CFuncLib::TimeToDataTime(m_PersonDisAppearTime).c_str());
	}	

compile error on gcc 4.8.5

Describe the bug
compile error

Your environment

  • OS: [CentOS Linux release 7.4.1708 x64]
  • Compiler [gcc 4.8.5]

Additional context

In file included from test.c:1:0:
yyjson.h:260:53: error: conflicting declaration ‘typedef long long int int64_t’
         __extension__ typedef long long             int64_t;
                                                     ^
In file included from /usr/include/stdlib.h:314:0,
                 from yyjson.h:18,
                 from test.c:1:
/usr/include/sys/types.h:197:1: error: ‘int64_t’ has a previous declaration as ‘typedef long int int64_t’
 __intN_t (64, __DI__);
 ^

Add YYJSON_TYPE_UNQUOTED

I'm planning on implementing this and would like your feedback.

Python supports arbitrarily large numbers (both integers and floats), and it would be nice to be able to support that with JSON input/output as well (as an optional flag, so by default it should raise an error).

I'd add support for this into yyjson as part of the work on py_yyjson by implementing a YYJSON_TYPE_UNQUOTED type. When reading, it parses until the end of a number and stores it in the string pool. Using this replaces all number parsing.

When writing, it's dumped as-is. Because it's not number specific, I've made it its own type instead of a numeric SUBTYPE. It's actually fairly useful for special cases, such as when I have a known-good JSON snippet I want to just sub in as an object value.

Winxp VC6 not support?

Describe the bug
A clear and concise description of what the bug is.

Your environment

  • OS: winxp
  • Compiler: vc6 sp6

Additional context
Add any other context about the problem here.

`
--------------------Configuration: testyyjson - Win32 Debug--------------------
Compiling...
yyjson.c
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(2534) : error C2485: 'noinline' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(2717) : error C2485: 'noinline' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(4020) : error C2485: 'noinline' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5181) : error C2485: 'align' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5181) : error C2059: syntax error : '('
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5594) : error C2485: 'noinline' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5604) : warning C4244: '=' : conversion from 'unsigned __int64 ' to 'unsigned int ', possible loss of data
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5888) : error C2485: 'align' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5888) : error C2059: syntax error : '('
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5957) : error C2485: 'align' : unrecognized extended attribute
c:\documents and settings\administrator\桌面\testyyjson\yyjson.c(5957) : error C2059: syntax error : '('
执行 cl.exe 时出错.

testyyjson.exe - 1 error(s), 0 warning(s)
`

Recursively parse an array of json objs

Hi!

I was just testing the library for the first time and couldn't find a way to parse something like the following:

{"people": [ {"name":"Alice", "age": 40}, {"name": "John", "age": 38} ]}

Is there a (optimal) way to do it?
Can you please provide an example on how to just iterate over multiple objs inside the array and print the "name" and "age"?

Thank you in advance!

forloop get string error

Hello, I'm trying to add some strings and integers to the for loop, but it's not working.It contains some empty strings
`
for (auto& c : row) {
auto obj = yyjson_mut_arr_add_obj(write_doc, write_val);
yyjson_mut_obj_add_int(write_doc, obj, "id", c[0].as_int());
yyjson_mut_obj_add_str(write_doc, obj, "username", c[1].as_string().c_str());
yyjson_mut_obj_add_str(write_doc, obj, "nick", c[2].as_string().c_str());
fmt::print("{}\n", c[1].as_string().c_str());
yyjson_mut_obj_add_val(write_doc, obj, "phone", yyjson_mut_strn(write_doc, c[3].as_string().c_str(), c[3].as_string().length()));
yyjson_mut_obj_add_val(write_doc, obj, "weixin", yyjson_mut_strn(write_doc, c[4].as_string().c_str(), c[4].as_string().length()));
yyjson_mut_obj_add_int(write_doc, obj, "createtime", c[5].as_int());
yyjson_mut_obj_add_int(write_doc, obj, "logintime", c[6].as_int());
yyjson_mut_obj_add_val(write_doc, obj, "loginrecord", yyjson_mut_strn(write_doc, c[7].as_string().c_str(), c[7].as_string().length()));
yyjson_mut_obj_add_val(write_doc, obj, "usermessage", yyjson_mut_strn(write_doc, c[8].as_string().c_str(), c[8].as_string().length()));
}

`

The result of fmt::print is correct and it prints the corresponding string
4565464 sadasdsa
However, the output of 'username' Json looks like this. Why
{"code":0,"data":[{"id":1,"username":"\u0000adasds","nick":"","phone":"\u000056","weixin":"","createtime":0,"logintime":0,"loginrecord":"","usermessage":""},{"id":2,"username":"\u0000adasdsa","nick":"","phone":"\u000056","weixin":"","createtime":0,"logintime":0,"loginrecord":"","usermessage":""}]}

His first 'username' should be 4565464 and his second 'username' should be 'sadasdsa' but now they are both \u0000adasds which is strange

How to serialize and deserialize JSON with sub-object arrays like this?

Example 1:

{
	"deviceId":"11223344",
	"eventType":"Alarm",
	"value":
	[
	    {
	        "type":"heart",
	        "upperLimit":"10:00:00",
	        "lowerLimit":"15:04:00"
	    }, 
	    {
	        "type":"heart",
	        "upperLimit":"17:01:00",
	        "lowerLimit":"21:15:00"
	    }
      ]
}

Example 2:

{
    "deviceId":"11223344",
    "eventType":"deviceEvent",
    "value":
    [
        {
            "type":"online",
            "value":"yes"
        }
    ]
}

一般的JSON解析用您的yyjson库非常方便,但是这种复杂点的没有例子,不会用了,希望作者能在百忙中回复一下

The trailing comma under the secondary object causes the json to not be parsed

Describe the bug
A clear and concise description of what the bug is.
二级对象下的逗号会导致json无法解析,虽然我知道这不符合json规范
{"objet":["ooject1":123,"ooject2":123,"ooject3":123,]}

Your environment

  • OS: [e.g. Ubuntu 20.04 x64]
  • Compiler: [e.g. gcc 9.3]
    centos7

Additional context
Add any other context about the problem here.

yyjson_read_opts crashses if the allocator pool is too small

Describe the bug
full repro

#include <stdio.h>
#include <string.h>
#include <yyjson.h>

int main()
{
        char json[] = "{\"alg\":\"ES256\",\"typ\":\"JWT\"}\0\0\0\0\0";
        char BUF[32]; // change me to >= 64 and I won't crash
        yyjson_alc json_alc;
        yyjson_alc_pool_init(&json_alc, BUF, sizeof(BUF));

        yyjson_doc* doc = yyjson_read_opts(json, strlen(json), YYJSON_READ_INSITU, &json_alc, NULL);
        if (!doc) {
                printf("OOM\n");
                return -1;
        }
        const char* alg = yyjson_get_str(yyjson_obj_get(yyjson_doc_get_root(doc), "alg"));
        printf("%s\n", alg);
        return 0;
}

Stack:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x0000000000429c76 in read_root_minify (err=0x7fffffffa130, flg=1, alc=..., end=0x7fffffffd61b "", cur=0x7fffffffd600 "{\"alg\":\"ES256\",\"typ\":\"JWT\"}", hdr=0x7fffffffd600 "{\"alg\":\"ES256\",\"typ\":\"JWT\"}") at src/yyjson.c:4705
#2  yyjson_read_opts (dat=0x7fffffffd600 "{\"alg\":\"ES256\",\"typ\":\"JWT\"}", len=27, flg=1, alc_ptr=0x7fffffffd5c0, err=0x7fffffffa130) at src/yyjson.c:5535
#3  0x00000000004012ce in main () at crash.c:12

If BUF's size is 64 then we correctly get a NULL document.

Your environment

  • OS: openSuse Tumbleweed
  • Compiler: gcc 12.1.1

Additional context
Reproduces on ee9e6d5d3bde4a26b78e9cfd596dd28d231266fa

The yyjson_obj_getn function increases the range limit.

The yyjson_obj_getn function requires linear search time when searching. Whether to consider increasing the range and limiting the start position and end position of the search, so that all node traversals caused by expandable fields can be avoided during use.

YYJSON_WRITE_ALLOW_INF_AND_NAN is not disabled when YYJSON_DISABLE_NON_STANDARD is active

As mentioned in #79, yyjson_val_write_opts and yyjson_mut_val_write_opts should include


#if YYJSON_DISABLE_NON_STANDARD
    flg &= ~YYJSON_WRITE_ALLOW_INF_AND_NAN;
#endif

like it is done in reader functions.

Currenly just adding this block is not enough, the tests don't check the YYJSON_DISABLE_NON_STANDARD before testing writer with YYJSON_WRITE_ALLOW_INF_AND_NAN flag.

memcpy 2 4 maybe slower than "repeat2_incr"

Please use English to communicate so that people from other countries can understand it.
i heared about memcpy when copy little string is slower than =
because of the memcpy use call instrustion.
on my computer memcpy 2 is slower than "repeat2_incr"
memcpytest.txt

Support writing Inf and NaN values as null, instead of returning an error.

Hello, could you add a support of writing Inf and NaN values as null, maybe via a writer flag?

Currently a apply this patch for yyjson in my project to achieve this:

diff --git a/src/yyjson.c b/src/yyjson.c
index 6a571b5..dc973a3 100644
--- a/src/yyjson.c
+++ b/src/yyjson.c
@@ -5635,7 +5635,8 @@ static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, bool allow_nan_and_inf) {
                 return buf + 3;
             }
         } else {
-            return NULL;
+            *(v32 *)&buf[0] = v32_make('n', 'u', 'l', 'l');
+            return buf + 4;
         }
     }
     
@@ -5779,7 +5780,8 @@ static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, bool allow_nan_and_inf) {
                 return buf + 3;
             }
         } else {
-            return NULL;
+            *(v32 *)&buf[0] = v32_make('n', 'u', 'l', 'l');
+            return buf + 4;
         }
     }
 }

yyjson_read_opts doesn't accept const char *

Any reason why yyjson_read_opts doesn't accept const char *?
I have to cast:

yyjson_doc *parse(std::string_view inputString) {
    yyjson_read_flag flags = 0;
    yyjson_read_err error;
    auto jsonDoc = yyjson_read_opts((char *)inputString.data(), inputString.length(), flags, nullptr, &error);

compile error on CentOS 6.10

Describe the bug
compile error on CentOS 6.10.

Your environment

  • OS: [CentOS 6.10 x64]
  • Compiler: [gcc 4.4.7]

Additional context

[root@localhost build]# cmake --build .
Scanning dependencies of target yyjson
[ 50%] Building C object CMakeFiles/yyjson.dir/src/yyjson.c.o
In file included from /home/yyjson-0.3.0/src/yyjson.c:9:
/home/yyjson-0.3.0/src/yyjson.h:298: error: conflicting types for ‘int64_t’
/usr/include/sys/types.h:198: note: previous declaration of ‘int64_t’ was here
/home/yyjson-0.3.0/src/yyjson.h:353: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/home/yyjson-0.3.0/src/yyjson.h:3900: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/home/yyjson-0.3.0/src/yyjson.c:26: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
/home/yyjson-0.3.0/src/yyjson.c:7213: warning: expected [error|warning|ignored] after ‘#pragma GCC diagnostic’
gmake[2]: *** [CMakeFiles/yyjson.dir/src/yyjson.c.o] Error 1
gmake[1]: *** [CMakeFiles/yyjson.dir/all] Error 2
gmake: *** [all] Error 2
[root@localhost build]# 

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.