JsonTreeModel API  v0.1.0
A Qt tree model for visualizing and manipulating a JSON document.
jsontreemodel.h
Go to the documentation of this file.
1 /*\
2  * Copyright (c) 2018 Sze Howe Koh
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 \*/
8 
9 #ifndef JSONTREEMODEL_H
10 #define JSONTREEMODEL_H
11 
12 #include <QAbstractItemModel>
13 #include <QJsonObject>
14 #include <QJsonArray>
15 
16 //=================================
17 // JsonTreeModelNode and subclasses
18 //=================================
19 /*
20  If only we could get a QJsonValueRef to a nested item,
21  we wouldn't need such "heavy" node classes...
22 */
23 /*
24  TODO: Support user-defined Structure and Scalar column names
25  TODO: Support user-defined icons for different datatypes in Structure column???
26  TODO: Support user-defined font for Array indices in Structure column???
27 */
28 class JsonTreeModelNode
29 {
30 public:
31  enum Type {
32  Scalar,
33  Object,
34  Array
35  };
36 
37  JsonTreeModelNode(JsonTreeModelNode* parent) : m_parent(parent) {}
38  virtual ~JsonTreeModelNode() {}
39 
40  inline JsonTreeModelNode* parent() const
41  { return m_parent; }
42 
43  inline void setParent(JsonTreeModelNode* parent)
44  { Q_ASSERT(parent->type() != Scalar); m_parent = parent; }
45 
46  virtual Type type() const = 0;
47  virtual QJsonValue value() const = 0;
48 
49 private:
50  // NOTE: Only JsonTreeModelListNode can be a parent, but I don't want to introduce a dependency to a subclass
51  JsonTreeModelNode* m_parent;
52 };
53 
54 class JsonTreeModelScalarNode : public JsonTreeModelNode
55 {
56 public:
57  JsonTreeModelScalarNode(const QJsonValue& value, JsonTreeModelNode* parent);
58 
59  QJsonValue value() const override
60  { return m_value; }
61 
62  void setValue(const QJsonValue& value)
63  { m_value = value; }
64 
65  Type type() const override
66  { return Scalar; }
67 
68 private:
69  QJsonValue m_value;
70 };
71 
72 class JsonTreeModelListNode : public JsonTreeModelNode
73 {
74 public:
75  JsonTreeModelListNode(JsonTreeModelNode* parent) : JsonTreeModelNode(parent) {}
76  JsonTreeModelListNode(const QJsonArray& array, JsonTreeModelNode* parent);
77 
78  ~JsonTreeModelListNode() override
79  {
80  // TODO: Tell parent to remove this child from its list? Only if we do partial deletions
81  qDeleteAll(m_childList);
82  }
83 
84  inline JsonTreeModelNode* childAt(int i) const
85  { return m_childList[i]; }
86 
87  inline int childCount() const
88  { return m_childList.count(); }
89 
90  inline int childPosition(JsonTreeModelNode* child) const
91  { return m_childPositions.value(child, -1); }
92 
93  Type type() const override
94  { return Array; }
95 
96  QJsonValue value() const override;
97 
98 protected:
99  void registerChild(JsonTreeModelNode* child);
100  void deregisterChild(JsonTreeModelNode* child);
101 
102 private:
103  QVector<JsonTreeModelNode*> m_childList;
104  QMap<JsonTreeModelNode*, int> m_childPositions;
105 };
106 
107 class JsonTreeModelNamedListNode : public JsonTreeModelListNode
108 {
109 public:
110  JsonTreeModelNamedListNode(const QJsonObject& object, JsonTreeModelNode* parent);
111 
112  inline QString childListNodeName(JsonTreeModelNode* child) const
113  { return m_childListNodeNames[child]; }
114 
115  inline int namedScalarCount() const
116  { return m_namedScalarMap.size(); }
117 
118  inline QJsonValue namedScalarValue(const QString& name) const
119  { return m_namedScalarMap[name]; }
120 
121  inline void setNamedScalarValue(const QString& name, const QJsonValue& value)
122  {
123  Q_ASSERT(value.type() != QJsonValue::Undefined && value.type() != QJsonValue::Array && value.type() != QJsonValue::Object);
124  m_namedScalarMap[name] = value;
125  }
126 
127  Type type() const override
128  { return Object; }
129 
130  QJsonValue value() const override;
131 
132 private:
133  // TODO: Use JsonTreeModelListNode::childPosition() for indexing; not need for map with m_childListNodeNames
134  QMap<JsonTreeModelNode*, QString> m_childListNodeNames;
135  QMap<QString, QJsonValue> m_namedScalarMap;
136 };
137 
138 class JsonTreeModelWrapperNode : public JsonTreeModelListNode
139 {
140 public:
141  JsonTreeModelWrapperNode(JsonTreeModelNamedListNode* realNode);
142 
143  QJsonValue value() const override
144  { return childAt(0)->value(); } // ASSUMPTION: A wrapper node will always have exactly 1 child JsonTreeModelNamedListNode
145 };
146 
147 
148 //=================================
149 // JsonTreeModel itself
150 //=================================
151 class JsonTreeModel : public QAbstractItemModel
152 {
153  Q_OBJECT
154 
155 public:
156  // TODO: Add flag to sort the keys, or leave them in the order of discovery
158  {
162  };
163 
164  explicit JsonTreeModel(QObject* parent = nullptr);
165  ~JsonTreeModel() override { delete m_rootNode; }
166 
167  // Header:
168  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
169 
170  // Basic functionality:
171  QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
172  QModelIndex parent(const QModelIndex& index) const override;
173 
174  int rowCount(const QModelIndex& parent = QModelIndex()) const override;
175  int columnCount(const QModelIndex& parent = QModelIndex()) const override;
176 
177  QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
178 
179  // Editable:
180  bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
181  Qt::ItemFlags flags(const QModelIndex& index) const override;
182 
183  // API specific to JsonTreeModel:
184  void setJson(const QJsonArray& array, ScalarColumnSearchMode searchMode = QuickSearch);
185  void setJson(const QJsonObject& object, ScalarColumnSearchMode searchMode = QuickSearch);
186  QJsonValue json(const QModelIndex& index = QModelIndex()) const;
187 
188  // TODO: Decide if the json()/setJson() API should be symmetrical or not
189 
190  void setScalarColumns(const QStringList& columns);
191  QStringList scalarColumns() const { return m_headers.mid(2); }
192 
193 private:
194  bool isEditable(const QModelIndex& index) const;
195 
196  JsonTreeModelListNode* m_rootNode;
197  QStringList m_headers;
198 };
199 
200 #endif // JSONTREEMODEL_H
The JsonTreeModel class provides a data model for a JSON document.
Definition: jsontreemodel.h:151
QJsonValue json(const QModelIndex &index=QModelIndex()) const
Returns the JSON value under the given index.
Definition: jsontreemodel.cpp:805
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Returns data under the given index for the specified role.
Definition: jsontreemodel.cpp:602
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows under the given parent.
Definition: jsontreemodel.cpp:558
ScalarColumnSearchMode
This enum controls how setJson() updates the model&#39;s column headers.
Definition: jsontreemodel.h:157
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Definition: jsontreemodel.cpp:491
Definition: jsontreemodel.h:159
~JsonTreeModel() override
Destroys the JsonTreeModel and frees its memory.
Definition: jsontreemodel.h:165
QModelIndex parent(const QModelIndex &index) const override
Definition: jsontreemodel.cpp:529
Definition: jsontreemodel.h:160
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: jsontreemodel.cpp:727
void setJson(const QJsonArray &array, ScalarColumnSearchMode searchMode=QuickSearch)
Sets the whole model&#39;s internal data structure to the given JSON array.
Definition: jsontreemodel.cpp:846
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Definition: jsontreemodel.cpp:660
QStringList scalarColumns() const
Returns the names of the JSON objects&#39; scalar members that are shown by the model.
Definition: jsontreemodel.h:191
JsonTreeModel(QObject *parent=nullptr)
Constructs an empty JsonTreeModel with the given parent.
Definition: jsontreemodel.cpp:474
void setScalarColumns(const QStringList &columns)
Sets the JSON objects&#39; scalar members that are shown by the model.
Definition: jsontreemodel.cpp:918
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Definition: jsontreemodel.cpp:505
Definition: jsontreemodel.h:161
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns in the model.
Definition: jsontreemodel.cpp:580