Parsing JSON Files and Storing Data in Program

Intro / What I Tried
I'm trying to parse JSON files (which I've never done before). I first tried JsonCpp using this tutorial:
https://www.codeproject.com/Articles/1102603/Accessing-JSON-Data-with-Cplusplus#_articleTop
But Json::Reader is deprecated and didn't seem to work.

I then tried looking into nlohmann's Parser which is nice because it only requires 1 header file to include:
https://github.com/nlohmann/json/tree/v3.6.1#stl-like-access
But I couldn't figure out how to get it to work. It gives errors when I include the header file as seen here:
https://github.com/nlohmann/json/issues/1659

Then I looked into how to create my own JSON parser and as I'm sure you can guess, that was insanely difficult (for a beginner). :P

So... After spending ~4 days trying to figure this out, now I'm here.

What I am trying to do:
There's a game called Minecraft that is Survival based and allows you to craft different items and build or use them for various things.

In short, I'm trying to parse the data and store the data into my program.

Here's an example JSON file - acacia_boat.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "type": "crafting_shaped",
  "group": "boat",
  "pattern": [
    "# #",
    "###"
  ],
  "key": {
    "#": {
      "item": "minecraft:acacia_planks"
    }
  },
  "result": {
    "item": "minecraft:acacia_boat"
  }
}

Another Example - piston.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "type": "crafting_shaped",
  "pattern": [
    "TTT",
    "#X#",
    "#R#"
  ],
  "key": {
    "R": {
      "item": "minecraft:redstone"
    },
    "#": {
      "item": "minecraft:cobblestone"
    },
    "T": {
      "tag": "minecraft:planks"
    },
    "X": {
      "item": "minecraft:iron_ingot"
    }
  },
  "result": {
    "item": "minecraft:piston"
  }
}

https://minecraft.gamepedia.com/Recipe#JSON_format

How can I parse the data and print it in my program?

Thanks!

Please Note: I may take 1-4 days to respond because I'm going to be very busy with a school project. But I'll check this when I can!

Edit: Apologies, this project is taking much longer than I expected.. I'll read and respond to all replies when I can. I'm going to be swamped for the week though as the semester is nearing the end.
Last edited on
Ill take nlohmann's word that this is a compiler warning (he posted on github a few moments you you made the issue).

I use clang I am am used to disabling warnings using compiler specific flags. For visual studios it should be the same, this is the first result on stack overflow I found:

1
2
3
4
#pragma warning( push )
#pragma warning( disable : 4101)
// Your function
#pragma warning( pop )  


The warning ID should be the one you see in your IDE.
You can do a quick-and-dirty "parse" of those particular json files like this. If that's the format of all of them (or something very similar that you could modify this technique for) then it would work just fine.

Assumptions: the input is of this form and has no errors and the strings have no embedded double-quotes. Also, I unilaterally decided that there is no distinction between the words "item" and "tag" in the "key" section and so just ignored them.

A map may be more appropriate for the "key" data. It depends on what you want to do with it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <cstdlib>

struct Minecraft {
    std::string type, group;    
    std::vector<std::string> pattern;
    std::vector<std::pair<std::string, std::string>> key;
    std::string result;

    Minecraft() = default;
    Minecraft(std::istream&);
    void read(std::istream&);
    void print();
    void clear() {
        type.clear();
        group.clear();
        pattern.clear();
        key.clear();
        result.clear();
    }
};

void die(const std::string& msg) {
    std::cerr << "Error: " << msg << '\n';
    exit(1);
}

void Minecraft::print() {
    using std::cout;
    cout << "type: " << type << '\n';
    if (!group.empty()) cout << "group: " << group << '\n';
    cout << "pattern:\n";
    for (const auto& p: pattern) cout << "  " << p << '\n';
    cout << "key:\n";
    for (const auto& k: key) cout << "  " << k.first << ": " << k.second << '\n';
    cout << "result: " << result << '\n';
}

std::string get_next_string(std::istream& in) {
    char ch;
    while (in.get(ch) && ch != '"') ;
    std::string str;
    while (in.get(ch) && ch != '"') str.push_back(ch);
    return str;
}

Minecraft::Minecraft(std::istream& in) {
    read(in);
}

void Minecraft::read(std::istream& in) {
    clear();
    std::string str = get_next_string(in);
    if (str != "type") die("type");
    type = get_next_string(in);
    str = get_next_string(in);
    if (str == "group") {
        group = get_next_string(in);
        str = get_next_string(in);
    }
    if (str != "pattern") die("pattern");
    for (;;) {
        str = get_next_string(in);
        if (str == "key") break;
        pattern.push_back(str);
    }
    for(;;) {
        std::pair<std::string,std::string> p;
        p.first = get_next_string(in);
        if (p.first == "result") break;
        str = get_next_string(in);
        if (str != "item" && str != "tag") die("key");
        p.second = get_next_string(in);
        key.push_back(p);
    }
    str = get_next_string(in);
    if (str != "item" && str != "tag") die("result");
    result = get_next_string(in);
}

int main(int argc, char **argv) {
    Minecraft mc;

    if (argc == 2) {
        std::ifstream fin(argv[1]);
        if (!fin) {
            std::cerr << "Cannot open " << argv[1] << '\n';
            return 1;
        }
        mc.read(fin);
    }
    else {
        extern std::istringstream sin;
        mc.read(sin);
    }

    mc.print();
}

std::istringstream sin(
R"(
{
  "type": "crafting_shaped",
  "pattern": [
    "TTT",
    "#X#",
    "#R#"
  ],
  "key": {
    "R": {
      "item": "minecraft:redstone"
    },
    "#": {
      "item": "minecraft:cobblestone"
    },
    "T": {
      "tag": "minecraft:planks"
    },
    "X": {
      "item": "minecraft:iron_ingot"
    }
  },
  "result": {
    "item": "minecraft:piston"
  }
}
)"); 

Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, char **argv) {
    extern std::istringstream sin;
    std::istream* in = &sin;

    if (argc == 2) {
        std::ifstream fin(argv[1]);
        if (!fin) {
            std::cerr << "Cannot open " << argv[1] << '\n';
            return 1;
        }
        in = &fin;
    }

    Minecraft mc(*in);
    mc.print();
}


IINM, that main function leaves in a dangling pointer when its referent, fin, is destroyed at the closing brace of the if-statement.
Yikes! You're right. I threw that bit in at the end so it could be run online with sin. Thanks. I'll fix it above.
EDIT: I decided to get rid of the pointer. It was, and is, pointless.
Last edited on
no comments XD

edit:
Well it doesn't really matter, the code nothing is wrong with it, but OP needs to reply if the problem was the fact that he thought compiler warnings were errors.

Your suggestion is a bit weird though, why reinvent the wheel when json is a powerful tool that can be reused.
Last edited on
If you mean that my code has no comments, I tried adding them (to the read function since everything else is self-explanatory, and if it isn't you can always ask) but it just added clutter. I should probably state the basic idea of the algorithm, though.

It only looks at the strings and ignores everything else (all the braces, colons, etc.). It reads strings with the get_next_string function that finds the next double-quote and reads the string up to the following double-quote.

So it sees this:
{
  "type": "crafting_shaped",
  "group": "boat",
  "pattern": [
    "# #",
    "###"
  ],
  "key": {
    "#": {
      "item": "minecraft:acacia_planks"
    }
  },
  "result": {
    "item": "minecraft:acacia_boat"
  }
}

as this:
type
crafting_shaped
group
boat
pattern
# #
###
key
#
item
minecraft:acacia_planks
result
item
minecraft:acacia_boat

It "parses" the data by knowing that the first string should be "type", the next the actual type string, the next is optionally "group", in which case the next is the group string, then there's "pattern", which is followed by strings that need to be stored in the pattern vector until the string "key", which is followed by pairs of strings (ignoring words "item" and "tag" which seem to have no meaning) that are stored in the key vector, until the string "result", which is followed by the result string (after "item" which we ignore as usual).
Why don't you use boost property_tree and their json parser:

https://www.boost.org/doc/libs/1_70_0/doc/html/property_tree/parsers.html#property_tree.parsers.json_parser

1
2
3
    boost::property_tree::ptree pt;
    try { boost::property_tree::read_json("filename", pt); }
    catch(...) { std::cerr << "Error"; }


and learn to use that libraries:

https://www.boost.org/doc/libs/1_70_0/

There are a lot useful things.
Topic archived. No new replies allowed.