Skip to content

Commit

Permalink
Merge pull request #121 from oblivioncth/feature/json_file_parse
Browse files Browse the repository at this point in the history
Add parseJson overloads for direct from file
  • Loading branch information
oblivioncth authored Aug 11, 2023
2 parents 7efad99 + e7e7b0a commit 199ad53
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 26 deletions.
12 changes: 1 addition & 11 deletions lib/core/doc/res/snippets/qx-json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,11 @@ struct MyJson

int main()
{
// Get JSON data
QFile jsonFile("data.json");
Q_ASSERT(jsonFile.open(QIODevice::ReadOnly));

QByteArray jsonData = jsonFile.readAll();
jsonFile.close();

MyJson myJsonDoc;

// Parse raw JSON data
QJsonDocument jd = QJsonDocument::fromJson(jsonData);
Q_ASSERT(!jd.isEmpty());

// Parse into custom structures
Qx::JsonError je = Qx::parseJson(myJsonDoc, jd);
Qx::JsonError je = Qx::parseJson(myJsonDoc, jsonFile);
Q_ASSERT(!je.isValid());
}
//! [1]
Expand Down
74 changes: 66 additions & 8 deletions lib/core/include/qx/core/qx-json.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ class File
{
private:
QString mIdentifier;
QString mFileError;

public:
File(const QString& filename);
File(const QFile& docFile);
File(const QFileInfo& docFile);
File(const QString& filename, const QString& fileError = {});
File(const QFile& docFile, const QString& fileError = {});
File(const QFileInfo& docFile, const QString& fileError = {});

QString string() const;
};
Expand Down Expand Up @@ -157,10 +158,13 @@ class QX_CORE_EXPORT JsonError final : public AbstractError<"Qx::JsonError", 5>
enum Form
{
NoError = 0,
MissingKey = 1,
TypeMismatch = 2,
EmptyDoc = 3,
InvalidValue = 4
MissingKey,
TypeMismatch,
EmptyDoc,
InvalidValue,
MissingFile,
InaccessibleFile,
FileReadError
};

//-Class Variables-------------------------------------------------------------
Expand All @@ -170,7 +174,10 @@ class QX_CORE_EXPORT JsonError final : public AbstractError<"Qx::JsonError", 5>
{MissingKey, u"The key does not exist."_s},
{TypeMismatch, u"Value type mismatch."_s},
{EmptyDoc, u"The document is empty."_s},
{InvalidValue, u"Invalid value for type."_s}
{InvalidValue, u"Invalid value for type."_s},
{MissingFile, u"File does not exist."_s},
{InaccessibleFile, u"Cannot open the file."_s},
{FileReadError, u"File read error."_s}
};

//-Instance Variables-------------------------------------------------------------
Expand Down Expand Up @@ -208,6 +215,7 @@ namespace QxJsonPrivate
static inline const QString ERR_CONV_TYPE = u"JSON Error: Converting value to %1"_s;
static inline const QString ERR_NO_KEY = u"JSON Error: Could not retrieve key '%1'."_s;
static inline const QString ERR_PARSE_DOC = u"JSON Error: Could not parse JSON document."_s;
static inline const QString ERR_READ_FILE = u"JSON Error: Could not read JSON file."_s;

//-Structs---------------------------------------------------------------
template<Qx::StringLiteral MemberN, typename MemberT, class Struct>
Expand Down Expand Up @@ -569,6 +577,56 @@ class QX_CORE_EXPORT QJsonParseErrorAdapter: public Qx::AbstractError<"QJsonPars
};

//-Functions-------------------------------------------------------------------------------------------------------
template<typename T>
requires json_root<T>
JsonError parseJson(T& parsed, const QString& filePath)
{
QFile file(filePath);

return parseJson(parsed, file);
}

template<typename T>
requires json_root<T>
JsonError parseJson(T& parsed, QFile& file)
{
if(!file.exists())
return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::MissingFile).withContext(QxJson::File(file.fileName()));

// Close and re-open file if missing required mode
if(!file.isReadable())
{
if(file.isOpen())
file.close();

if(!file.open(QIODevice::ReadOnly))
return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::InaccessibleFile).withContext(QxJson::File(file.fileName(), file.errorString()));
}

// Close file when finished
QScopeGuard fileGuard([&file]{ file.close(); });

// Read data
QByteArray jsonData = file.readAll();
if(jsonData.isEmpty())
{
if(file.error() != QFileDevice::NoError)
return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), file.errorString()));
else
return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::EmptyDoc).withContext(QxJson::File(file.fileName()));
}

// Basic parse
QJsonParseError jpe;
QJsonDocument jd = QJsonDocument::fromJson(jsonData, &jpe);

if(jpe.error != jpe.NoError)
return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), jpe.errorString()));

// True parse
return parseJson(parsed, jd).withContext(QxJson::File(file.fileName()));
}

template<typename T>
requires json_root<T>
JsonError parseJson(T& parsed, const QJsonDocument& doc)
Expand Down
53 changes: 46 additions & 7 deletions lib/core/src/qx-json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,26 @@ QString QJsonParseErrorAdapter::deriveSecondary() const { return OFFSET_STR.arg(

//-Functions---------------------------------------------------------------------------------------------
//Public:
/*!
* @fn JsonError parseJson(T& parsed, const QString& filePath)
*
* Parses the entire JSON document file at path @a filePath and stores the result in @a parsed.
* @a T must satisfy the @ref json_root concept.
*
* If parsing fails, a valid JsonError is returned that describes the cause; otherwise, an invalid
* error is returned.
*/

/*!
* @fn JsonError parseJson(T& parsed, QFile& file)
*
* Parses the entire JSON document file @a file and stores the result in @a parsed.
* @a T must satisfy the @ref json_root concept.
*
* If parsing fails, a valid JsonError is returned that describes the cause; otherwise, an invalid
* error is returned.
*/

/*!
* @fn JsonError parseJson(T& parsed, const QJsonDocument& doc)
*
Expand Down Expand Up @@ -346,24 +366,43 @@ namespace QxJson
*/

/*!
* Constructs a file node with the identifier of @a filename.
* Constructs a file node with the identifier of @a filename and file specific error
* information @a fileError, if any.
*/
File::File(const QString& filename) : mIdentifier(filename) {}
File::File(const QString& filename, const QString& fileError) :
mIdentifier(filename),
mFileError(fileError)
{}

/*!
* Constructs a file node with the identifier set to the filename of @a docFile.
* Constructs a file node with the identifier set to the filename of @a docFile and
* file specific error information @a fileError, if any.
*/
File::File(const QFile& docFile) : mIdentifier(docFile.fileName()) {}
File::File(const QFile& docFile, const QString& fileError) :
mIdentifier(docFile.fileName()),
mFileError(fileError)
{}

/*!
* Constructs a file node with the identifier set to the absolute path of @a docFile.
* Constructs a file node with the identifier set to the absolute path of @a docFile
* and file specific error information @a fileError, if any.
*/
File::File(const QFileInfo& docFile) : mIdentifier(docFile.absoluteFilePath()) {}
File::File(const QFileInfo& docFile, const QString& fileError) :
mIdentifier(docFile.absoluteFilePath()),
mFileError(fileError)
{}

/*!
* Returns the string representation of the node.
*/
QString File::string() const { return u"File: "_s + mIdentifier; }
QString File::string() const
{
QString str = u"File: "_s + mIdentifier;
if(!mFileError.isEmpty())
str += (u"\n [%1]"_s).arg(mFileError);

return str;
}

/*!
* @class Document
Expand Down

0 comments on commit 199ad53

Please sign in to comment.