Gemmi C++ API
Loading...
Searching...
No Matches
json.hpp
Go to the documentation of this file.
1// Copyright 2017 Global Phasing Ltd.
2//
3// Reading CIF-JSON (COMCIFS) and mmJSON (PDBj) formats into cif::Document.
4
5#ifndef GEMMI_JSON_HPP_
6#define GEMMI_JSON_HPP_
7#include <algorithm> // for move
8#include <cstdio> // for FILE
9#include <memory> // for unique_ptr
10#include <string>
11#include <vector>
12
13#define SAJSON_UNSORTED_OBJECT_KEYS
14#define SAJSON_NUMBERS_AS_STRINGS
15#include "third_party/sajson.h"
16
17#include "cifdoc.hpp" // for Document, etc
18#include "fail.hpp" // for fail, sys_fail
19#include "fileutil.hpp" // for read_file_into_buffer
20
21namespace gemmi {
22namespace cif {
23using std::size_t;
24
25inline std::string json_type_as_string(sajson::type t) {
26 switch (t) {
27 case sajson::TYPE_INTEGER: return "<integer>";
28 case sajson::TYPE_DOUBLE: return "<double>";
29 case sajson::TYPE_NULL: return "<null>";
30 case sajson::TYPE_FALSE: return "<false>";
31 case sajson::TYPE_TRUE: return "<true>";
32 case sajson::TYPE_STRING: return "<string>";
33 case sajson::TYPE_ARRAY: return "<array>";
34 case sajson::TYPE_OBJECT: return "<object>";
35 default: return "<unknown type>";
36 }
37}
38
39inline std::string as_cif_value(const sajson::value& val) {
40 switch (val.get_type()) {
41 case sajson::TYPE_DOUBLE:
42 return val.as_string();
43 case sajson::TYPE_NULL:
44 return "?";
45 case sajson::TYPE_FALSE:
46 return ".";
47 case sajson::TYPE_STRING:
48 return quote(val.as_string());
49 default:
50 fail("Unexpected ", json_type_as_string(val.get_type()), " in JSON.");
51 return "";
52 }
53}
54
55inline void fill_document_from_sajson(Document& d, const sajson::document& s) {
56 // assuming mmJSON here, we'll add handling of CIF-JSON later on
57 sajson::value root = s.get_root();
58 if (root.get_type() != sajson::TYPE_OBJECT || root.get_length() != 1)
59 fail("not mmJSON");
60 std::string block_name = root.get_object_key(0).as_string();
61 if (!starts_with(block_name, "data_"))
62 fail("not mmJSON - top level key should start with data_\n"
63 "(if you use gemmi-cif2json to write JSON, use -m for mmJSON)");
64 d.blocks.emplace_back(block_name.substr(5));
65 std::vector<Item>& items = d.blocks[0].items;
66 sajson::value top = root.get_object_value(0);
67 if (top.get_type() != sajson::TYPE_OBJECT)
68 fail("");
69 for (size_t i = 0; i != top.get_length(); ++i) {
70 std::string category_name = "_" + top.get_object_key(i).as_string() + ".";
71 sajson::value category = top.get_object_value(i);
72 if (category.get_type() != sajson::TYPE_OBJECT ||
73 category.get_length() == 0 ||
74 category.get_object_value(0).get_type() != sajson::TYPE_ARRAY)
75 fail("");
76 size_t cif_cols = category.get_length();
77 size_t cif_rows = category.get_object_value(0).get_length();
78 if (cif_rows > 1) {
79 items.emplace_back(LoopArg{});
80 Loop& loop = items.back().loop;
81 loop.tags.reserve(cif_cols);
82 loop.values.resize(cif_cols * cif_rows);
83 }
84 for (size_t j = 0; j != cif_cols; ++j) {
85 std::string tag = category_name + category.get_object_key(j).as_string();
86 sajson::value arr = category.get_object_value(j);
87 if (arr.get_type() != sajson::TYPE_ARRAY)
88 fail("Expected array, got " + json_type_as_string(arr.get_type()));
89 if (arr.get_length() != cif_rows)
90 fail("Expected array of length ", std::to_string(cif_rows), " not ",
91 std::to_string(arr.get_length()));
92 if (cif_rows == 1) {
93 items.emplace_back(tag, as_cif_value(arr.get_array_element(0)));
94 } else {
95 Loop& loop = items.back().loop;
96 loop.tags.emplace_back(std::move(tag));
97 for (size_t k = 0; k != cif_rows; ++k)
98 loop.values[j + k*cif_cols] = as_cif_value(arr.get_array_element(k));
99 }
100 }
101 }
102}
103
104// reads mmJSON file mutating the input buffer as a side effect
105inline Document read_mmjson_insitu(char* buffer, size_t size,
106 const std::string& name="mmJSON") {
108 sajson::document json = sajson::parse(sajson::dynamic_allocation(),
109 sajson::mutable_string_view(size, buffer));
110 if (!json.is_valid())
111 fail(name + ":", std::to_string(json.get_error_line()), " error: ",
112 json.get_error_message_as_string());
114 doc.source = name;
115 return doc;
116}
117
118inline Document read_mmjson_file(const std::string& path) {
120 return read_mmjson_insitu(buffer.data(), buffer.size(), path);
121}
122
123template<typename T>
125 std::string name = input.is_stdin() ? "stdin" : input.path();
126 CharArray buffer = read_into_buffer(std::forward<T>(input));
127 return read_mmjson_insitu(buffer.data(), buffer.size(), name);
128}
129
130} // namespace cif
131} // namespace gemmi
132#endif
std::string as_cif_value(const sajson::value &val)
Definition json.hpp:39
void fill_document_from_sajson(Document &d, const sajson::document &s)
Definition json.hpp:55
std::string quote(std::string v)
Definition cifdoc.hpp:1162
Document read_mmjson_insitu(char *buffer, size_t size, const std::string &name="mmJSON")
Definition json.hpp:105
Document read_mmjson(T &&input)
Definition json.hpp:124
Document read_mmjson_file(const std::string &path)
Definition json.hpp:118
std::string json_type_as_string(sajson::type t)
Definition json.hpp:25
CharArray read_into_buffer(T &&input)
Definition fileutil.hpp:122
bool starts_with(const std::string &str, const std::string &prefix)
Definition util.hpp:38
CharArray read_file_into_buffer(const std::string &path)
Definition fileutil.hpp:98
void fail(const std::string &msg)
Definition fail.hpp:59
std::vector< std::string > tags
Definition cifdoc.hpp:129
std::vector< std::string > values
Definition cifdoc.hpp:130