LINUX.ORG.RU

json-c: JSON parsing

 ,


1

3

Здравствуйте.

Есть JSON выхлоп с yahoo weather такого вида:

{
   "query": {
     "count": 1,
     "created": "2016-06-27T13:12:50Z",
     "lang": "en-US",
     "results": {
       "channel": {
         "units": {
           ...
         },
         ...
         "location": {
           "city": "City",
           "country": "Country",
           "region": "Region"
         },
         "wind": {
           ...
         },
         ...
         "item": {
           ...
           "condition": {
             "code": "28",
             "date": "Mon, 27 Jun 2016 03:00 PM EEST",
             "temp": "30",
             "text": "Mostly Cloudy"
           },
...

Мне нужны значения query->results->channel->location->city query->results->channel->item->condition->{temp, text}. Как это сделать более красиво, используя json-c библиотеку, нежели в этом говнокоде:

static void get_json_items(struct json_object *jobj, const char *key)
{
    struct json_object *tmp;
    struct json_object *location;
    struct json_object *condition;
    struct json_object *temp;
    struct json_object *text;
    
    int exists;

    /* enum json_type type; */

    exists = json_object_object_get_ex(jobj, key, &tmp);
    if (!exists) {
        printf("%s is not found in JSON\n", key);
        return;
    }
    exists = json_object_object_get_ex(tmp, "results", &tmp);
    if (!exists) {
        printf("\"results\" is not found\n");
        return;
    }
    exists = json_object_object_get_ex(tmp, "channel", &tmp);
    if (!exists) {
        printf("\"channel\" is not found\n");
        return;
    }
    exists = json_object_object_get_ex(tmp, "location", &location);
    if (!exists) {
        printf("\"location\" is not found\n");
        return;
    }

    exists = json_object_object_get_ex(location, "city", &location);
    if (!exists) {
        printf("\"city\" is not found\n");
        return;
    }
    
    exists = json_object_object_get_ex(tmp, "item", &tmp);
    if (!exists) {
        printf("\"item\" is not found\n");
        return;
    }

    exists = json_object_object_get_ex(tmp, "condition", &condition);
    if (!exists) {
        printf("\"condition\" is not found\n");
        return;
    }

    exists = json_object_object_get_ex(condition, "temp", &temp);
    if (!exists) {
        printf("\"temp\" is not found\n");
        return;
    }

    exists = json_object_object_get_ex(condition, "text", &text);
    if (!exists) {
        printf("\"text\" is not found\n");
        return;
    }

    
    printf("DBG >>>\n");
    /* printf("---\nlocation: %s\n---\n", json_object_to_json_string(location)); */
    /* printf("---\nitem: %s\n---\n", json_object_to_json_string(tmp)); */

    printf("city: %s\n",
           json_object_get_string(location)
           );

    printf("temp: %s\n",
           json_object_get_string(temp)
           );

    printf("text: %s\n",
           json_object_get_string(text)
           );
    
    printf("DBG <<<\n");

    
}

Вот сам выхлоп бинарника:

gcc -Wall -Wextra -Wpedantic -std=c99 -I/usr/include/json-c/ -g3 -lcurl -ljson-c foo.c -o foo && ./foo
foo.c: In function 'main':
foo.c:218:25: warning: unused variable 'tmp' [-Wunused-variable]
     struct json_object *tmp;
                         ^~~
DBG >>>
city: City
temp: 23
text: Cloudy
DBG <<<


Как это сделать более красиво, используя json-c библиотеку, нежели в этом говнокоде

Увы, а иначе никак. Это С.

joy4eg ★★★★★
()

А в чем тут говнокод?

Как еще реализовать работу с данными, ориентироваными, под объектную модель, без заточки парсера под конкретный набор данных?

RiseOfDeath ★★★★
()

Либо сделай враппера себе, что-то типа

bool json_object_get_by_query(const json_object *obj, char **q, json_object **val)
{
    json_object *curr = obj;
    json_object *value = NULL;

    for (const char *key = *q; *q; q++)
    {
       if (json_object_object_get_ex(curr, key, &value))
       {
          curr = value;
       }
       else
       {
          return false; /* not found */
       }
    }
    *val = curr;
    return true;
}

int main()
{
    char *q[] = {"aa", "bb", "cc", NULL};

    json_object_get_by_query(..., q, ...);
}

joy4eg ★★★★★
()
Ответ на: комментарий от joy4eg

спасибо, буду велосипедить ☹

ubik
() автор топика

как вариант, использовать jansson - там это делается за один вызов json_unpack() : https://jansson.readthedocs.io/en/2.7/apiref.html?highlight=json_array_append...

Пример оттуда же:

/* root is the JSON array [[1, 2], {"baz": null} */
json_error_t error;
json_unpack_ex(root, &error, JSON_VALIDATE_ONLY, "[[i,i], {s:n}]", "baz");

eagleivg ★★★★★
()
Последнее исправление: eagleivg (всего исправлений: 1)

Циклом же, ну!

(еще можно свелосипедить какой-нибудь json query language, но обычно это не нужно, и вообще я такого не предлагал!)

staseg ★★★★★
()

Кстати, эта долбанная либа довольно-таки хорошо течет, поэтому-то я, замучившись с этими фекалиями, забульбенил себе простой парсер. Если что-то больше понадобится, добавлю. Зато не течет и очевидно работает.

anonymous
()
Ответ на: комментарий от anonymous

хм, у меня не течёт пока

==22019== HEAP SUMMARY:
==22019==     in use at exit: 0 bytes in 0 blocks
==22019==   total heap usage: 7,073 allocs, 7,073 frees, 715,353 bytes allocated

ubik
() автор топика
Ответ на: комментарий от anonymous

Кстати, эта долбанная либа довольно-таки хорошо течет

Это где оно течет ? Пруф, Эдди, мы хотим пруф!

joy4eg ★★★★★
()

оформить в виде массива строк и цикла по ним.

waker ★★★★★
()

https://github.com/codemeow/camelion

Сначала меняете : на => и можно пользовать:

CML_Node * node;
if (CML_StorableFromFile(yourfilename, &node)) return;

CML_Node * target;
if (CML_NodeFindString(node, "results.channel.location.city.item.condition.temp.text", &target)) return;

printf("%s\n", target->data.string);
someoneelsenotme
()
Ответ на: комментарий от joy4eg

Про течку json-c все интернеты пестрят. А еще эти кони поломали обратную совместимость.

Ну их в пень! Я лучше свой велосипед буду юзать.

anonymous
()
Ответ на: комментарий от ubik

Возьми большой JSON, поначитай оттуда всякой фигни, закрой, освободи структуры, да сравни используемую память до и после. Возможно, течку починили, но еще год назад была жесть!

anonymous
()
Ответ на: комментарий от anonymous

возможно, течку починили, но еще год назад была жесть!

Использовал его года полтора назад, и как совсем не текло ;)

joy4eg ★★★★★
()
Ответ на: комментарий от joy4eg

Читай:

На определенном моменте баг json-c, который вылезал изредка (т.к. json-c течет, иной раз появляются необъяснимые ошибки free() или malloc()), стал появляться постоянно при некоторых входных параметрах!

Я полдня убил на то, чтобы выяснить, какого черта вроде бы правильный код выбрасывал мне ошибку при попытке malloc'а или free. Этот код при использовании json-c то работал нормально, то вдруг блокировал последующие вызовы malloc/free.

А еще из-за бешеной запутанности (часть функций автоматом освобождают память, часть — нет) возможно такое.

anonymous
()

Если ты используешь сишечку для парсинга погоды, ты что-то делаешь не так.

A1
()

На flex'е с bison'ом. Без зависимостей

Это файл yw.l для flex'а:

%{
#include <stdlib.h>
#include "yw.tab.h"
%}
%%
[{},:] {return yytext[0];}
"[" {return yytext[0];}
"]" {return yytext[0];}
["]"query"["] {return S_query;}
["]"results"["] {return S_results;}
["]"channel"["] {return S_channel;}
["]"location"["] {return S_location;}
["]"city"["] {return S_city;}
["]"item"["] {return S_item;}
["]"condition"["] {return S_condition;}
["]"temp"["] {return S_temp;}
["]"text"["] {return S_text;}
["]"<![CDATA["[^\x5d]*"]]>"["] {yytext[yyleng-1]='\0';
  yylval.str=yytext+1; return S;}
["][^"]*"\"" {yytext[yyleng-1]='\0';
  yylval.str=yytext+1; return S;}
[0-9]+ {yytext[yyleng]='\0';yylval.num=atol(yytext);return N;}
[\n] {;}
. {;}
%%
int yywrap(){return 1;}

Это в файл yw.y , для bison'а

%{
#include <stdio.h>
%}
%union {
  long num;
  char *str;
}
%token S_query S_results S_channel
  S_location S_city
  S_item S_condition S_temp S_text
%token<str> S
%token<num> N
%start json
%%
json: | json l | json h ;
l: '[' a1 ']' ;
h: '{' a2 '}' ;
v: S | N | l | h ;
a1: v | a1 ',' v ;
a2: S ':' v | S_text ':' v | S_query ':' '{' b2 '}'
 | a2 ',' S ':' v | a2 ',' S_text ':' v | a2 ',' S_query ':' '{' b2 '}' ;

b2: S ':' v | S_results ':' '{' b3 '}'
 | b2 ',' S ':' v | b2 ',' S_results ':' '{' b3 '}' ;
b3: S ':' v | S_channel ':' '{' b4 '}'
 | b3 ',' S ':' v | b3 ',' S_channel ':' '{' b4 '}' ;
b4: S ':' v | S_location ':' '{' b5 '}' | S_item ':' '{' b6 '}'
 | b4 ',' S ':' v | b4 ',' S_location ':' '{' b5 '}'
 | b4 ',' S_item ':' '{' b6 '}' ;

b5: S ':' v | S_city ':' S {printf("city: '%s'\n",$3);}
 | b5 ',' S ':' v | b5 ',' S_city ':' S {printf("city: '%s'\n",$5);} ;

b6: S ':' v | S_condition ':' '{' b7 '}'
 | b6 ',' S ':' v | b6 ',' S_condition ':' '{' b7 '}' ;
b7: S ':' v
 | S_temp ':' S {printf("temp: '%s'\n",$3);}
 | S_text ':' S {printf("text: '%s'\n",$3);}
 | b7 ',' S ':' v
 | b7 ',' S_temp ':' S {printf("temp: '%s'\n",$5);}
 | b7 ',' S_text ':' S {printf("text: '%s'\n",$5);}
;
%%
int yyerror(char *er){fprintf(stderr,"er:'%s'\n",er);return 0;}
int main(){yyparse();return 0;}

Сборка и запуск:

bison -d yw.y
flex yw.l
cc -o yw yw.tab.c lex.yy.c

./yw <yah_we.json

Бизоний исходник получился длинный т.к. в него запихнуто определение заходов в ветки (иначе на strcmp - «хрен редьки не слаще»).

anonymous
()
Ответ на: комментарий от ubik

Исходник рабочий, быстрый, не течет. Попробуйте соберите, зацените. Выковыривает указанные поля из json-ов уано-погоды. Модификиция - редактированием. Что еще рассказать ?

anonymous
()
Ответ на: комментарий от anonymous

Покажи код, который у *ТЕБЯ* течет, и версию json-c на которой оно воспроизводиться. Пока что Я вижу пару рукожопов которые не осилили прочитать man.

joy4eg ★★★★★
()
Ответ на: комментарий от anonymous

да, я до сегодняшнего дня вообще в глаза не видел ни bison, ни flex. Спасибо за наводку, буду курить доки :)

ubik
() автор топика
Ответ на: комментарий от joy4eg

не осилили прочитать man

В json-c так черезжопно все сделано, что этот его «ман» — лютый трындец! Ну его нафиг, проще свое написать, чем в этом ужасе разбираться!!!

anonymous
()

Зачем писать велосипед? Используй jq. Какой выходной продукт?

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.