Skip to content

Commit

Permalink
Add JSON read support.
Browse files Browse the repository at this point in the history
  • Loading branch information
kwokcb committed Aug 24, 2023
1 parent 1ab3bc2 commit e29d409
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 5 deletions.
140 changes: 138 additions & 2 deletions source/MaterialXFormat/JSONIo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
MATERIALX_NAMESPACE_BEGIN

const string JSON_EXTENSION = "json";
const string JSON_MIME_TYPE = "application/mtlx+json";
using json = nlohmann::json;

namespace
{

// Writing utility
void elementToJSON(ConstElementPtr elem, json& jsonObject, const JSONWriteOptions* writeOptions)
{
ElementPredicate elementPredicate = nullptr;
ElementPredicate elementPredicate;
bool addDefinitionInformation = true;
bool storeLayoutInformation = true;
bool addNodeGraphChildren = true;
Expand Down Expand Up @@ -101,6 +102,139 @@ namespace
// Add new element to parent
jsonObject[elem->getName()] = jsonElem;
}

// Reading utilities
void elementFromJSON(const json& node, ElementPtr elem, const JSONReadOptions* readOptions,
const string& indent)
{
if (node.is_object())
{
for (const auto& entry : node.items())
{
const std::string& key = entry.key();
const json& value = entry.value();

// Handle attributes
if (value.is_string())
{
string attrName = key;
if (attrName.at(0) == '@')
attrName.erase(0, 1);

const StringSet skipKeys = { "name", "category" };
if (!skipKeys.count(key))
{
//std::cout << indent << "Set Attribute: " << attrName << " to: " << value << std::endl;
elem->setAttribute(attrName, string(value));
}
}
else
{
string name = string(key);

// Check for duplicate elements.
ConstElementPtr previous = elem->getChild(name);
if (!previous)
{
// Create a new element of the proper category
string category = EMPTY_STRING;
for (const auto& e : value.items())
{
const std::string& k = e.key();
if (k == "@category")
{
category = string(e.value());
}
}

if (!category.empty())
{
//std::cout << indent << "Create Element <" << category << "> Name: " << name << std::endl;
ElementPtr child = elem->addChildOfCategory(category, name);
elementFromJSON(value, child, readOptions, indent + string(" "));
}
}
}
}
}
}

void documentFromJSON(DocumentPtr doc,
const json& jsonDoc,
const JSONReadOptions* readOptions = nullptr)
{
const string indent = " ";
//if (jsonDoc.is_object())
{
// Go through all top level items
for (const auto& entry: jsonDoc.items())
{
// Handle top level attrs here.

const string& key = entry.key();
const json& value = entry.value();
if (value.is_string())
{
string attrName = key;
if (attrName.at(0) == '@')
attrName.erase(0, 1);
//std::cout << indent << "Set Doc Attribute: " << attrName << " to: " << value << std::endl;
doc->setAttribute(attrName, string(value));
}
else
{
elementFromJSON(value, doc, readOptions, indent);
}
}
elementFromJSON(jsonDoc, doc, readOptions, indent);
if (!readOptions || readOptions->upgradeVersion)
{
doc->upgradeVersion();
}
}
}
}

//
// Reading
//
void readFromJSONString(DocumentPtr doc, const string& buffer, const JSONReadOptions* readOptions)
{
std::stringstream inputStream;
inputStream << buffer;

readFromJSONStream(doc, inputStream, readOptions);
}

void readFromJSONStream(DocumentPtr doc, std::istream& stream, const JSONReadOptions* readOptions)
{
json jsonDoc;
try
{
jsonDoc = json::parse(stream);
}
catch (const json::parse_error& e) {
std::cerr << "JSON parsing error: " << e.what() << std::endl;
}

documentFromJSON(doc, jsonDoc, readOptions);
}

void readFromJSONFile(DocumentPtr doc,
FilePath filename,
FileSearchPath searchPath,
const JSONReadOptions* readOptions)
{
searchPath.append(getEnvironmentPath());
filename = searchPath.find(filename);

std::ifstream inputFile;
inputFile.open(filename.asString());
if (!inputFile.is_open()) {
throw std::runtime_error(string("Unable to open JSON file:") + filename.asString());
}

readFromJSONStream(doc, inputFile, readOptions);
}

//
Expand All @@ -110,6 +244,8 @@ namespace
void writeToJSONStream(DocumentPtr doc, std::ostream& stream, const JSONWriteOptions* writeOptions)
{
json materialXRoot;
materialXRoot["mimtype"] = "application/mtlx+json";

json documentRoot = json::object();

for (const string& attrName : doc->getAttributeNames())
Expand Down
79 changes: 77 additions & 2 deletions source/MaterialXFormat/JSONIo.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,26 @@

MATERIALX_NAMESPACE_BEGIN

class XmlReadOptions;
class JSONReadOptions;

extern MX_FORMAT_API const string JSON_EXTENSION;
extern MX_FORMAT_API const string JSON_MIME_TYPE;

/// @class JSONReadOptions
/// A set of options for controlling the behavior of JSON read functions.
class MX_FORMAT_API JSONReadOptions
{
public:
JSONReadOptions() {};
~JSONReadOptions() { }

/// If true, then documents from earlier versions of MaterialX will be upgraded
/// to the current version. Defaults to true.
bool upgradeVersion = true;
};

/// @class JSONWriteOptions
/// A set of options for controlling the behavior of JSON write functions.
class MX_FORMAT_API JSONWriteOptions
{
public:
Expand All @@ -46,14 +61,74 @@ class MX_FORMAT_API JSONWriteOptions
unsigned int indent = 4;
};

/// @name Write Functions
/// @name Read Functions
/// @{

/// Read a Document as JSON from the given character buffer.
/// @param doc The Document into which data is read.
/// @param buffer The string buffer from which data is read.
/// @param readOptions An optional pointer to an JSONReadOptions object.
/// If provided, then the given options will affect the behavior of the
/// read function. Defaults to a null pointer.
/// @throws ExceptionParseError if the document cannot be parsed.
MX_FORMAT_API void readFromJSONString(DocumentPtr doc, const string& buffer, const JSONReadOptions* readOptions = nullptr);

/// Read a Document as JSON from the given input stream.
/// @param doc The Document into which data is read.
/// @param stream The input stream from which data is read.
/// @param readOptions An optional pointer to an JSONReadOptions object.
/// If provided, then the given options will affect the behavior of the
/// read function. Defaults to a null pointer.
/// @throws ExceptionParseError if the document cannot be parsed.
MX_FORMAT_API void readFromJSONStream(DocumentPtr doc, std::istream& stream, const JSONReadOptions* readOptions = nullptr);

/// Read a Document as JSON from the given filename.
/// @param doc The Document into which data is read.
/// @param filename The filename from which data is read. This argument can
/// be supplied either as a FilePath or a standard string.
/// @param readOptions An optional pointer to an JSONReadOptions object.
/// If provided, then the given options will affect the behavior of the read
/// function. Defaults to a null pointer.
/// @throws ExceptionParseError if the document cannot be parsed.
/// @throws ExceptionFileMissing if the file cannot be opened.
MX_FORMAT_API void readFromJSONFile(DocumentPtr doc,
FilePath filename,
FileSearchPath searchPath = FileSearchPath(),
const JSONReadOptions* readOptions = nullptr);

/// @}
/// @name Write Functions

/// Write a Document as JSON to the given filename.
/// @param doc The Document to be written.
/// @param filename The filename to which data is written. This argument can
/// be supplied either as a FilePath or a standard string.
/// @param writeOptions An optional pointer to an JSONWriteOptions object.
/// If provided, then the given options will affect the behavior of the
/// write function. Defaults to a null pointer.
MX_FORMAT_API void writeToJSONFile(DocumentPtr doc, const FilePath& filename, const JSONWriteOptions* writeOptions = nullptr);

/// Write a Document as JSON to a new string, returned by value.
/// @param doc The Document to be written.
/// @param writeOptions An optional pointer to an JSONWriteOptions object.
/// If provided, then the given options will affect the behavior of the
/// write function. Defaults to a null pointer.
/// @return The output string, returned by value
MX_FORMAT_API string writeToJSONString(DocumentPtr doc, const JSONWriteOptions* writeOptions = nullptr);

/// Write a Document as JSON to the given output stream.
/// @param doc The Document to be written.
/// @param stream The output stream to which data is written
/// @param writeOptions An optional pointer to an JSONWriteOptions object.
/// If provided, then the given options will affect the behavior of the
/// write function. Defaults to a null pointer.
MX_FORMAT_API void writeToJSONStream(DocumentPtr doc, std::ostream& stream, const JSONWriteOptions* writeOptions);

/// @}

/// @}


MATERIALX_NAMESPACE_END

#endif
10 changes: 9 additions & 1 deletion source/MaterialXGraphEditor/Graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ mx::DocumentPtr Graph::loadDocument(mx::FilePath filename)
try
{
readFromXmlFile(doc, resolvedFilename, searchPath, options);
readFromJSONFile(doc, resolvedFilename);
}
catch (mx::Exception& e)
{
Expand All @@ -176,7 +177,14 @@ mx::DocumentPtr Graph::loadDocument(mx::FilePath filename)
{
if (!filename.isEmpty())
{
mx::readFromXmlFile(doc, filename, _searchPath, &readOptions);
if (filename.getExtension() == "json")
{
mx::readFromJSONFile(doc, filename, _searchPath);
}
else
{
mx::readFromXmlFile(doc, filename, _searchPath, &readOptions);
}
doc->importLibrary(_stdLib);
std::string message;
if (!doc->validate(&message))
Expand Down

0 comments on commit e29d409

Please sign in to comment.