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