Skip to content

Commit 39f8978

Browse files
authored
Merge pull request #14258 from rintaro/basic-json-null
[JSONSerialization] Add ability to emit 'null' value.
2 parents 2965c8f + d747010 commit 39f8978

File tree

6 files changed

+255
-205
lines changed

6 files changed

+255
-205
lines changed

include/swift/Basic/JSONSerialization.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,30 @@ struct ScalarTraits {
110110
};
111111

112112

113+
/// This class should be specialized by any type that can be 'null' in JSON.
114+
/// For example:
115+
///
116+
/// template<>
117+
/// struct NullableTraits<MyType *> > {
118+
/// static bool isNull(MyType *&ptr) {
119+
/// return !ptr;
120+
/// }
121+
/// static MyType &get(MyType *&ptr) {
122+
/// return *ptr;
123+
/// }
124+
/// };
125+
template<typename T>
126+
struct NullableTraits {
127+
// Must provide:
128+
//
129+
// Function to return true if the value is 'null'.
130+
// static bool isNull(const T &Val);
131+
//
132+
// Function to return a reference to the unwrapped value.
133+
// static T::value_type &get(const T &Val);
134+
};
135+
136+
113137
/// This class should be specialized by any type that needs to be converted
114138
/// to/from a JSON array. For example:
115139
///
@@ -249,6 +273,23 @@ template<typename T>
249273
struct has_ArrayTraits : public std::integral_constant<bool,
250274
has_ArrayMethodTraits<T>::value > { };
251275

276+
// Test if NullableTraits<T> is defined on type T.
277+
template <class T>
278+
struct has_NullableTraits
279+
{
280+
typedef bool (*Signature_isNull)(T&);
281+
282+
template <typename U>
283+
static char test(SameType<Signature_isNull, &U::isNull> *);
284+
285+
template <typename U>
286+
static double test(...);
287+
288+
public:
289+
static bool const value =
290+
(sizeof(test<NullableTraits<T>>(nullptr)) == 1);
291+
};
292+
252293
inline bool isNumber(StringRef S) {
253294
static const char DecChars[] = "0123456789";
254295
if (S.find_first_not_of(DecChars) == StringRef::npos)
@@ -285,6 +326,7 @@ struct missingTraits : public std::integral_constant<bool,
285326
!has_ScalarEnumerationTraits<T>::value
286327
&& !has_ScalarBitSetTraits<T>::value
287328
&& !has_ScalarTraits<T>::value
329+
&& !has_NullableTraits<T>::value
288330
&& !has_ObjectTraits<T>::value
289331
&& !has_ArrayTraits<T>::value> {};
290332

@@ -338,6 +380,7 @@ class Output {
338380
void endBitSetScalar();
339381

340382
void scalarString(StringRef &, bool);
383+
void null();
341384

342385
template <typename T>
343386
void enumCase(T &Val, const char* Str, const T ConstVal) {
@@ -579,6 +622,16 @@ jsonize(Output &out, T &Val, bool) {
579622
}
580623

581624

625+
template<typename T>
626+
typename std::enable_if<has_NullableTraits<T>::value,void>::type
627+
jsonize(Output &out, T &Obj, bool) {
628+
if (NullableTraits<T>::isNull(Obj))
629+
out.null();
630+
else
631+
jsonize(out, NullableTraits<T>::get(Obj), true);
632+
}
633+
634+
582635
template<typename T>
583636
typename std::enable_if<validatedObjectTraits<T>::value, void>::type
584637
jsonize(Output &out, T &Val, bool) {

lib/Basic/JSONSerialization.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ using namespace swift;
2020
unsigned Output::beginArray() {
2121
StateStack.push_back(ArrayFirstValue);
2222
Stream << '[';
23-
if (PrettyPrint) {
24-
Stream << '\n';
25-
}
2623
return 0;
2724
}
2825

2926
bool Output::preflightElement(unsigned, void *&) {
3027
if (StateStack.back() != ArrayFirstValue) {
3128
assert(StateStack.back() == ArrayOtherValue && "We must be in a sequence!");
3229
Stream << ',';
33-
if (PrettyPrint)
34-
Stream << '\n';
3530
}
36-
if (PrettyPrint)
31+
if (PrettyPrint) {
32+
Stream << '\n';
3733
indent();
34+
}
3835
return true;
3936
}
4037

@@ -46,8 +43,9 @@ void Output::postflightElement(void*) {
4643
}
4744

4845
void Output::endArray() {
46+
bool HadContent = StateStack.back() != ArrayFirstValue;
4947
StateStack.pop_back();
50-
if (PrettyPrint) {
48+
if (PrettyPrint && HadContent) {
5149
Stream << '\n';
5250
indent();
5351
}
@@ -66,13 +64,12 @@ bool Output::canElideEmptyArray() {
6664
void Output::beginObject() {
6765
StateStack.push_back(ObjectFirstKey);
6866
Stream << "{";
69-
if (PrettyPrint)
70-
Stream << '\n';
7167
}
7268

7369
void Output::endObject() {
70+
bool HadContent = StateStack.back() != ObjectFirstKey;
7471
StateStack.pop_back();
75-
if (PrettyPrint) {
72+
if (PrettyPrint && HadContent) {
7673
Stream << '\n';
7774
indent();
7875
}
@@ -86,11 +83,11 @@ bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
8683
if (StateStack.back() != ObjectFirstKey) {
8784
assert(StateStack.back() == ObjectOtherKey && "We must be in an object!");
8885
Stream << ',';
89-
if (PrettyPrint)
90-
Stream << '\n';
9186
}
92-
if (PrettyPrint)
87+
if (PrettyPrint) {
88+
Stream << '\n';
9389
indent();
90+
}
9491
Stream << '"' << Key << "\":";
9592
if (PrettyPrint)
9693
Stream << ' ';
@@ -222,6 +219,10 @@ void Output::scalarString(StringRef &S, bool MustQuote) {
222219
Stream << S;
223220
}
224221

222+
void Output::null() {
223+
Stream << "null";
224+
}
225+
225226
void Output::indent() {
226227
Stream.indent(StateStack.size() * 2);
227228
}

test/Syntax/Inputs/serialize_multiple_decls.json

Lines changed: 25 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,12 @@
1818
"layout": [
1919
{
2020
"kind": "AttributeList",
21-
"layout": [
22-
23-
],
21+
"layout": [],
2422
"presence": "Missing"
2523
},
2624
{
2725
"kind": "DeclModifier",
28-
"layout": [
29-
30-
],
26+
"layout": [],
3127
"presence": "Missing"
3228
},
3329
{
@@ -65,9 +61,7 @@
6561
"kind": "identifier",
6662
"text": "A"
6763
},
68-
"leadingTrivia": [
69-
70-
],
64+
"leadingTrivia": [],
7165
"trailingTrivia": [
7266
{
7367
"kind": "Space",
@@ -78,23 +72,17 @@
7872
},
7973
{
8074
"kind": "GenericParameterClause",
81-
"layout": [
82-
83-
],
75+
"layout": [],
8476
"presence": "Missing"
8577
},
8678
{
8779
"kind": "TypeInheritanceClause",
88-
"layout": [
89-
90-
],
80+
"layout": [],
9181
"presence": "Missing"
9282
},
9383
{
9484
"kind": "GenericWhereClause",
95-
"layout": [
96-
97-
],
85+
"layout": [],
9886
"presence": "Missing"
9987
},
10088
{
@@ -104,19 +92,13 @@
10492
"tokenKind": {
10593
"kind": "l_brace"
10694
},
107-
"leadingTrivia": [
108-
109-
],
110-
"trailingTrivia": [
111-
112-
],
95+
"leadingTrivia": [],
96+
"trailingTrivia": [],
11397
"presence": "Present"
11498
},
11599
{
116100
"kind": "DeclList",
117-
"layout": [
118-
119-
],
101+
"layout": [],
120102
"presence": "Present"
121103
},
122104
{
@@ -129,9 +111,7 @@
129111
"value": 1
130112
}
131113
],
132-
"trailingTrivia": [
133-
134-
],
114+
"trailingTrivia": [],
135115
"presence": "Present"
136116
}
137117
],
@@ -144,12 +124,8 @@
144124
"tokenKind": {
145125
"kind": "semi"
146126
},
147-
"leadingTrivia": [
148-
149-
],
150-
"trailingTrivia": [
151-
152-
],
127+
"leadingTrivia": [],
128+
"trailingTrivia": [],
153129
"presence": "Missing"
154130
}
155131
],
@@ -163,16 +139,12 @@
163139
"layout": [
164140
{
165141
"kind": "AttributeList",
166-
"layout": [
167-
168-
],
142+
"layout": [],
169143
"presence": "Missing"
170144
},
171145
{
172146
"kind": "DeclModifier",
173-
"layout": [
174-
175-
],
147+
"layout": [],
176148
"presence": "Missing"
177149
},
178150
{
@@ -198,9 +170,7 @@
198170
"kind": "identifier",
199171
"text": "B"
200172
},
201-
"leadingTrivia": [
202-
203-
],
173+
"leadingTrivia": [],
204174
"trailingTrivia": [
205175
{
206176
"kind": "Space",
@@ -211,23 +181,17 @@
211181
},
212182
{
213183
"kind": "GenericParameterClause",
214-
"layout": [
215-
216-
],
184+
"layout": [],
217185
"presence": "Missing"
218186
},
219187
{
220188
"kind": "TypeInheritanceClause",
221-
"layout": [
222-
223-
],
189+
"layout": [],
224190
"presence": "Missing"
225191
},
226192
{
227193
"kind": "GenericWhereClause",
228-
"layout": [
229-
230-
],
194+
"layout": [],
231195
"presence": "Missing"
232196
},
233197
{
@@ -237,19 +201,13 @@
237201
"tokenKind": {
238202
"kind": "l_brace"
239203
},
240-
"leadingTrivia": [
241-
242-
],
243-
"trailingTrivia": [
244-
245-
],
204+
"leadingTrivia": [],
205+
"trailingTrivia": [],
246206
"presence": "Present"
247207
},
248208
{
249209
"kind": "DeclList",
250-
"layout": [
251-
252-
],
210+
"layout": [],
253211
"presence": "Present"
254212
},
255213
{
@@ -262,9 +220,7 @@
262220
"value": 1
263221
}
264222
],
265-
"trailingTrivia": [
266-
267-
],
223+
"trailingTrivia": [],
268224
"presence": "Present"
269225
}
270226
],
@@ -277,12 +233,8 @@
277233
"tokenKind": {
278234
"kind": "semi"
279235
},
280-
"leadingTrivia": [
281-
282-
],
283-
"trailingTrivia": [
284-
285-
],
236+
"leadingTrivia": [],
237+
"trailingTrivia": [],
286238
"presence": "Missing"
287239
}
288240
],
@@ -307,9 +259,7 @@
307259
"value": 1
308260
}
309261
],
310-
"trailingTrivia": [
311-
312-
],
262+
"trailingTrivia": [],
313263
"presence": "Present"
314264
}
315265
],

0 commit comments

Comments
 (0)