1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3 * Copyright (C) 2014 Emweb bv, Herent, Belgium.
4 *
5 * See the LICENSE file for terms of use.
6 */
7 #ifndef WT_DBO_JSON_H_
8 #define WT_DBO_JSON_H_
9
10 #include <ostream>
11 #include <sstream>
12 #include <type_traits>
13
14 #include <Wt/Dbo/ptr.h>
15 #include <Wt/Dbo/weak_ptr.h>
16 #include <Wt/Dbo/collection.h>
17 #include <Wt/Dbo/Field.h>
18 #include <Wt/Dbo/Session.h>
19
20 namespace Wt {
21 namespace Dbo {
22 class EscapeOStream;
23
24 /*! \class JsonSerializer Wt/Dbo/Json.h Wt/Dbo/Json.h
25 * \brief An action to serialize objects to JSON.
26 *
27 * This class is an \p Action that serializes objects to an ostream.
28 * These objects must implement the \p persist() method. It also has
29 * support for serializing \link ptr ptrs\endlink to these objects,
30 * std::vectors of \link ptr ptrs\endlink, and
31 * \link collection collections\endlink of \link ptr ptrs\endlink.
32 *
33 * It will follow one-to-one and \link Wt::Dbo::ManyToOne ManyToOne\endlink
34 * relations in one way: weak_ptr and \ref collection fields are followed
35 * and serialized, for \ref ptr fields only the \link Wt::Dbo::ptr::id() id\endlink
36 * is output.
37 *
38 * No extraneous whitespace is output.
39 *
40 * \ingroup dbo
41 */
42 class WTDBO_API JsonSerializer
43 {
44 public:
45 /*! \brief Creates a JsonSerializer that writes to an std::ostream.
46 *
47 * Note that the std::ostream is not flushed to automatically. The
48 * flush will happen automatically when this JsonSerializer is
49 * destructed.
50 */
51 JsonSerializer(std::ostream& out);
52
53 /*! \brief Destructor
54 */
55 virtual ~JsonSerializer();
56
session()57 Session *session() { return session_; }
58
59 template<typename T>
60 typename std::enable_if< !std::is_enum<T>::value, void>::type
61 act(FieldRef<T> field);
62
63 template<typename T>
64 typename std::enable_if< std::is_enum<T>::value, void>::type
act(FieldRef<T> field)65 act(FieldRef<T> field) {
66 writeFieldName(field.name());
67 out(static_cast<int>(field.value()));
68 }
69
70 void act(FieldRef<std::string> field);
71 void act(FieldRef<int> field);
72 void act(FieldRef<long long> field);
73 void act(FieldRef<bool> field);
74
75 template<typename T>
actId(T & value,const std::string & name,int size)76 void actId(T& value, const std::string& name, int size) {
77 field(*this, value, name, size);
78 }
79
80 template<typename T>
actId(ptr<T> & value,const std::string & name,int size,int fkConstraints)81 void actId(ptr<T>& value, const std::string& name, int size, int fkConstraints) {
82 field(*this, value, name, size);
83 }
84
85 template<typename T>
actPtr(const PtrRef<T> & field)86 void actPtr(const PtrRef<T>& field) {
87 writeFieldName(field.name());
88 if (field.value())
89 outputId(field.id());
90 else
91 out("null");
92 }
93
94 template<typename T>
actWeakPtr(const WeakPtrRef<T> & field)95 void actWeakPtr(const WeakPtrRef<T>& field) {
96 writeFieldName(session_->tableName<T>() + std::string("_") + field.joinName());
97 ptr<T> v = field.value().query();
98 if (v) {
99 serialize(v);
100 } else {
101 out("null");
102 }
103 }
104
105 template<typename T>
actCollection(const CollectionRef<T> & collec)106 void actCollection(const CollectionRef<T>& collec) {
107 if (collec.type() == ManyToOne) {
108 collection<ptr<T> > c = collec.value();
109 writeFieldName(session_->tableName<T>() + std::string("s_") + collec.joinName());
110 out('[');
111 bool first = true;
112 for (typename collection<ptr<T> >::const_iterator i = c.begin(); i != c.end(); ++i) {
113 if (first)
114 first = false;
115 else
116 out(',');
117 serialize(*i);
118 }
119 out(']');
120 }
121 }
122
getsValue()123 bool getsValue() const {
124 return true;
125 }
126
setsValue()127 bool setsValue() const {
128 return false;
129 }
130
isSchema()131 bool isSchema() const {
132 return false;
133 }
134
135 /*! \brief Serialize the given object.
136 *
137 * Serializes a plain object that implements the \p persist() method.
138 */
139 template<typename T>
serialize(const T & t)140 void serialize(const T& t) {
141 session_ = NULL;
142 out('{');
143 const_cast<T&>(t).persist(*this);
144 out('}');
145 }
146
147 /*! \brief Serialize the object that is pointed to by the given \ref ptr.
148 *
149 * This method does the same as the plain object serializer, but
150 * also adds an extra \link Wt::Dbo::ptr::id() id\endlink field.
151 */
152 template<typename T>
serialize(const ptr<T> & t)153 void serialize(const ptr<T>& t) {
154 session_ = t.session();
155 out('{');
156 first_ = true;
157 if (dbo_traits<T>::surrogateIdField()) {
158 out('"');
159 out(dbo_traits<T>::surrogateIdField());
160 out("\":");
161 outputId(t.id());
162 first_ = false;
163 }
164 const_cast<T&>(*t).persist(*this);
165 out('}');
166 }
167
168 /*! \brief Serialize an std::vector of \link ptr ptrs\endlink.
169 *
170 * Serializes each \ref ptr in the vector individually,
171 * and puts it in an \p Array.
172 */
173 template<typename T>
serialize(const std::vector<ptr<T>> & v)174 void serialize(const std::vector<ptr<T> >& v) {
175 out('[');
176 for (typename std::vector<ptr<T> >::const_iterator i = v.begin(); i != v.end(); ++i) {
177 if (i != v.begin())
178 out(',');
179 else
180 session_ = (*i).session();
181 serialize(*i);
182 }
183 out(']');
184 }
185
186 /*! \brief Serialize a \ref collection of \link ptr ptrs\endlink.
187 *
188 * Serializes each \ref ptr in the \ref collection individually,
189 * and puts it in an \p Array.
190 *
191 * The typical usage scenario of this method is to serialize
192 * the results of a query to JSON.
193 */
194 template<typename T>
serialize(const collection<ptr<T>> & c)195 void serialize(const collection<ptr<T> >& c) {
196 session_ = c.session();
197 out('[');
198 bool first = true;
199 for (typename collection<ptr<T> >::const_iterator i = c.begin(); i != c.end(); ++i) {
200 if (first)
201 first = false;
202 else
203 out(',');
204 serialize(*i);
205 }
206 out(']');
207 session_ = NULL;
208 }
209
210 private:
211 std::ostream &out_;
212 EscapeOStream *escapeOut_, *stringLiteral_;
213 bool first_;
214 Session *session_;
215
216 void out(char);
217 void out(const char *);
218 void out(int);
219 void out(long long);
220
221 template<typename T>
outputId(T id)222 void outputId(T id) {
223 std::stringstream ss;
224 ss << id;
225 fastJsStringLiteral(ss.str());
226 }
outputId(long long id)227 void outputId(long long id) {
228 out(id);
229 }
230
231 void writeFieldName(const std::string& fieldName);
232
233 void fastJsStringLiteral(const std::string& s);
234 };
235
236 /*! \brief Serialize the given object to the given ostream.
237 *
238 * \sa JsonSerializer::serialize()
239 */
240 template<typename C>
jsonSerialize(const C & c,std::ostream & out)241 void jsonSerialize(const C& c, std::ostream& out) {
242 JsonSerializer serializer(out);
243 serializer.serialize(c);
244 }
245
246 /*! \brief Serialize the object pointed to by the given \ref ptr
247 * to the given ostream.
248 *
249 * \sa JsonSerializer::serialize()
250 */
251 template<typename C>
jsonSerialize(const ptr<C> & c,std::ostream & out)252 void jsonSerialize(const ptr<C>& c, std::ostream& out) {
253 JsonSerializer serializer(out);
254 serializer.serialize(c);
255 }
256
257 /*! \brief Serialize a vector of \link ptr ptrs\endlink to the given
258 * ostream.
259 *
260 * \sa JsonSerializer::serialize()
261 */
262 template<typename C>
jsonSerialize(const std::vector<ptr<C>> & v,std::ostream & out)263 void jsonSerialize(const std::vector<ptr<C> >& v, std::ostream& out) {
264 JsonSerializer serializer(out);
265 serializer.serialize(v);
266 }
267
268 /*! \brief Serialize a \ref collection of \link ptr ptrs\endlink to the given
269 * ostream.
270 *
271 * \sa JsonSerializer::serialize()
272 */
273 template<typename C>
jsonSerialize(const collection<C> & c,std::ostream & out)274 void jsonSerialize(const collection<C>& c, std::ostream& out) {
275 JsonSerializer serializer(out);
276 serializer.serialize(c);
277 }
278
279 }
280 }
281
282 #endif // WT_DBO_JSON_H_
283