7 ex__json_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00

examples/ex_json.c

Functions

Name
int process_args(int argc, char ** argv)
int main(int argc, char ** argv)
void expect_true(const char * label, int cond)

Attributes

Name
int log_level
int failures

Functions Documentation

function process_args

static int process_args(
    int argc,
    char ** argv
)

function main

int main(
    int argc,
    char ** argv
)

function expect_true

static void expect_true(
    const char * label,
    int cond
)

Attributes Documentation

variable log_level

static int log_level = 3;

variable failures

static int failures = 0;

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "nilorea/n_json.h"
#include "nilorea/n_log.h"

static int log_level = LOG_ERR;
static int failures = 0;

static int process_args(int argc, char** argv) {
    int opt;
    while ((opt = getopt(argc, argv, "V:")) != -1) {
        switch (opt) {
            case 'V':
                if (!strcmp("LOG_NULL", optarg))
                    log_level = LOG_NULL;
                else if (!strcmp("LOG_NOTICE", optarg))
                    log_level = LOG_NOTICE;
                else if (!strcmp("LOG_INFO", optarg))
                    log_level = LOG_INFO;
                else if (!strcmp("LOG_ERR", optarg))
                    log_level = LOG_ERR;
                else if (!strcmp("LOG_DEBUG", optarg))
                    log_level = LOG_DEBUG;
                else
                    return -1;
                break;
            default:
                return -1;
        }
    }
    return 0;
}

static void expect_true(const char* label, int cond) {
    if (!cond) {
        n_log(LOG_ERR, "%s: condition false", label);
        failures++;
    }
}

int main(int argc, char** argv) {
    set_log_level(LOG_ERR);
    if (process_args(argc, argv) != 0) {
        fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
        return 2;
    }
    set_log_level(log_level);

    /* invalid JSON yields NULL */
    expect_true("invalid parse is NULL", n_json_parse("{ not json") == NULL);

    {
        const char* text =
            "{ \"name\": \"scan\", \"depth\": 3, \"active\": true,"
            "  \"meta\": { \"host\": \"example.test\" },"
            "  \"tags\": [ \"a\", \"b\", \"c\" ] }";
        N_JSON* root = n_json_parse(text);
        N_JSON* meta;
        N_JSON* tags;
        char* dup;
        expect_true("root parsed", root != NULL);
        if (!root)
            return 1;

        /* typed getters with defaults */
        expect_true("string", strcmp(n_json_string(root, "name", "?"), "scan") == 0);
        expect_true("string default", strcmp(n_json_string(root, "missing", "def"), "def") == 0);
        expect_true("int", n_json_int(root, "depth", -1) == 3);
        expect_true("int default", n_json_int(root, "missing", 7) == 7);
        expect_true("bool", n_json_bool(root, "active", 0) == 1);
        expect_true("bool default", n_json_bool(root, "missing", 1) == 1);

        /* string_dup returns an owned copy, NULL when absent */
        dup = n_json_string_dup(root, "name");
        expect_true("string_dup", dup && strcmp(dup, "scan") == 0);
        free(dup);
        expect_true("string_dup absent is NULL", n_json_string_dup(root, "missing") == NULL);

        /* nested object */
        meta = n_json_get(root, "meta");
        expect_true("nested object", n_json_is_object(meta));
        expect_true("nested string", strcmp(n_json_string(meta, "host", "?"), "example.test") == 0);

        /* array */
        tags = n_json_get(root, "tags");
        expect_true("array", n_json_is_array(tags));
        expect_true("array size", n_json_array_size(tags) == 3);
        expect_true("array elem", strcmp(n_json_as_string(n_json_array_at(tags, 1), "?"), "b") == 0);
        expect_true("array out of range", n_json_array_at(tags, 9) == NULL);

        n_json_free(&root);
        expect_true("free nulls", root == NULL);
    }

    /* parse a file */
    {
        const char* path = "ex_json_tmp.json";
        FILE* fp = fopen(path, "w");
        N_JSON* root;
        if (fp) {
            fputs("{ \"k\": \"v\", \"n\": 42 }", fp);
            fclose(fp);
        }
        expect_true("temp file written", fp != NULL);
        root = n_json_parse_file(path);
        expect_true("file parsed", root != NULL);
        if (root) {
            expect_true("file string", strcmp(n_json_string(root, "k", "?"), "v") == 0);
            expect_true("file int", n_json_int(root, "n", 0) == 42);
            n_json_free(&root);
        }
        unlink(path);
        expect_true("missing file is NULL", n_json_parse_file("ex_json_no_such.json") == NULL);
    }

    /* iteration, keys, numbers, length-bounded parse */
    {
        const char buf[] = "{ \"a\": 1.5, \"b\": [10, 20, 30], \"c\": false, \"d\": \"x\" }TRAILING";
        N_JSON* root = n_json_parse_len(buf, strlen(buf) - strlen("TRAILING"));
        N_JSON* it;
        const char* keys = "abcd";
        int i = 0;
        double v = 0.0, sum = 0.0;
        expect_true("parse_len ignores bytes past len", root != NULL);
        if (root) {
            for (it = n_json_first(root); it; it = n_json_next(it), i++)
                expect_true("keys in document order", i < 4 && n_json_key(it) && n_json_key(it)[0] == keys[i]);
            expect_true("four members", i == 4);
            expect_true("number", n_json_number(root, "a", 0.0) == 1.5);
            expect_true("number default", n_json_number(root, "d", -2.0) == -2.0);
            for (it = n_json_first(n_json_get(root, "b")); it; it = n_json_next(it))
                if (n_json_as_number(it, &v)) sum += v;
            expect_true("array walk", sum == 60.0);
            expect_true("array elements have no key", n_json_key(n_json_first(n_json_get(root, "b"))) == NULL);
            expect_true("is_number", n_json_is_number(n_json_get(root, "a")) && !n_json_is_number(n_json_get(root, "d")));
            expect_true("is_string", n_json_is_string(n_json_get(root, "d")));
            expect_true("as_bool", n_json_as_bool(n_json_get(root, "c"), 1) == 0);
            expect_true("as_bool default", n_json_as_bool(n_json_get(root, "a"), 7) == 7);
            expect_true("first of a scalar is NULL", n_json_first(n_json_get(root, "a")) == NULL);
            expect_true("as_number of a string", n_json_as_number(n_json_get(root, "d"), &v) == 0);
            n_json_free(&root);
        }
        expect_true("parse_len rejects a truncated document", n_json_parse_len(buf, 5) == NULL);
    }

    if (failures) {
        n_log(LOG_ERR, "ex_json: %d failure(s)", failures);
        return 1;
    }
    n_log(LOG_NOTICE, "ex_json: all checks passed");
    return 0;
}