-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move code from include/gemmi/json.hpp to src/json.cpp
- Loading branch information
Showing
4 changed files
with
117 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright Global Phasing Ltd. | ||
|
||
#include <gemmi/json.hpp> | ||
#include <algorithm> // for move | ||
|
||
#define SAJSON_UNSORTED_OBJECT_KEYS | ||
#define SAJSON_NUMBERS_AS_STRINGS | ||
#include "../third_party/sajson.h" | ||
|
||
namespace gemmi { | ||
namespace cif { | ||
using std::size_t; | ||
|
||
static std::string json_type_as_string(sajson::type t) { | ||
switch (t) { | ||
case sajson::TYPE_INTEGER: return "<integer>"; | ||
case sajson::TYPE_DOUBLE: return "<double>"; | ||
case sajson::TYPE_NULL: return "<null>"; | ||
case sajson::TYPE_FALSE: return "<false>"; | ||
case sajson::TYPE_TRUE: return "<true>"; | ||
case sajson::TYPE_STRING: return "<string>"; | ||
case sajson::TYPE_ARRAY: return "<array>"; | ||
case sajson::TYPE_OBJECT: return "<object>"; | ||
default: return "<unknown type>"; | ||
} | ||
} | ||
|
||
static std::string as_cif_value(const sajson::value& val) { | ||
switch (val.get_type()) { | ||
case sajson::TYPE_DOUBLE: | ||
return val.as_string(); | ||
case sajson::TYPE_NULL: | ||
return "?"; | ||
// mmJSON files from PDBj (this format has no spec) have special support | ||
// for boolean YES|NO, which is used only in category _em_specimen. | ||
// IMO it's a bad idea, but we must handle it if we want to read mmJSON. | ||
case sajson::TYPE_FALSE: | ||
return "NO"; // "." in CIF-JSON | ||
case sajson::TYPE_TRUE: | ||
return "YES"; | ||
case sajson::TYPE_STRING: | ||
return quote(val.as_string()); | ||
default: | ||
fail("Unexpected ", json_type_as_string(val.get_type()), " in JSON."); | ||
return ""; | ||
} | ||
} | ||
|
||
static void fill_document_from_sajson(Document& d, const sajson::document& s) { | ||
// assuming mmJSON here, we'll add handling of CIF-JSON later on | ||
sajson::value root = s.get_root(); | ||
if (root.get_type() != sajson::TYPE_OBJECT || root.get_length() != 1) | ||
fail("not mmJSON"); | ||
std::string block_name = root.get_object_key(0).as_string(); | ||
if (!starts_with(block_name, "data_")) | ||
fail("not mmJSON - top level key should start with data_\n" | ||
"(if you use gemmi-cif2json to write JSON, use -m for mmJSON)"); | ||
d.blocks.emplace_back(block_name.substr(5)); | ||
std::vector<Item>& items = d.blocks[0].items; | ||
sajson::value top = root.get_object_value(0); | ||
if (top.get_type() != sajson::TYPE_OBJECT) | ||
fail(""); | ||
for (size_t i = 0; i != top.get_length(); ++i) { | ||
std::string category_name = "_" + top.get_object_key(i).as_string() + "."; | ||
sajson::value category = top.get_object_value(i); | ||
if (category.get_type() != sajson::TYPE_OBJECT || | ||
category.get_length() == 0 || | ||
category.get_object_value(0).get_type() != sajson::TYPE_ARRAY) | ||
fail(""); | ||
size_t cif_cols = category.get_length(); | ||
size_t cif_rows = category.get_object_value(0).get_length(); | ||
if (cif_rows > 1) { | ||
items.emplace_back(LoopArg{}); | ||
Loop& loop = items.back().loop; | ||
loop.tags.reserve(cif_cols); | ||
loop.values.resize(cif_cols * cif_rows); | ||
} | ||
for (size_t j = 0; j != cif_cols; ++j) { | ||
std::string tag = category_name + category.get_object_key(j).as_string(); | ||
sajson::value arr = category.get_object_value(j); | ||
if (arr.get_type() != sajson::TYPE_ARRAY) | ||
fail("Expected array, got " + json_type_as_string(arr.get_type())); | ||
if (arr.get_length() != cif_rows) | ||
fail("Expected array of length ", std::to_string(cif_rows), " not ", | ||
std::to_string(arr.get_length())); | ||
if (cif_rows == 1) { | ||
items.emplace_back(tag, as_cif_value(arr.get_array_element(0))); | ||
} else { | ||
Loop& loop = items.back().loop; | ||
loop.tags.emplace_back(std::move(tag)); | ||
for (size_t k = 0; k != cif_rows; ++k) | ||
loop.values[j + k*cif_cols] = as_cif_value(arr.get_array_element(k)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Document read_mmjson_insitu(char* buffer, size_t size, const std::string& name) { | ||
Document doc; | ||
sajson::document json = sajson::parse(sajson::dynamic_allocation(), | ||
sajson::mutable_string_view(size, buffer)); | ||
if (!json.is_valid()) | ||
fail(name + ":", std::to_string(json.get_error_line()), " error: ", | ||
json.get_error_message_as_string()); | ||
fill_document_from_sajson(doc, json); | ||
doc.source = name; | ||
return doc; | ||
} | ||
|
||
|
||
|
||
} // namespace cif | ||
} // namespace gemmi |
File renamed without changes.