Skip to content

Commit e94fc8d

Browse files
authored
Merge pull request #458 from metafacture/457-implementJsonBooleansAndNumbers
Implement JSON booleans and numbers.
2 parents bdab7d3 + 86db950 commit e94fc8d

File tree

4 files changed

+349
-4
lines changed

4 files changed

+349
-4
lines changed

metafacture-json/src/main/java/org/metafacture/json/JsonDecoder.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
public final class JsonDecoder extends DefaultObjectPipe<String, StreamReceiver> {
5151

5252
public static final String DEFAULT_ARRAY_MARKER = JsonEncoder.ARRAY_MARKER;
53+
public static final String DEFAULT_BOOLEAN_MARKER = JsonEncoder.BOOLEAN_MARKER;
54+
public static final String DEFAULT_NUMBER_MARKER = JsonEncoder.NUMBER_MARKER;
5355

5456
public static final String DEFAULT_ARRAY_NAME = "%d";
5557

@@ -62,6 +64,8 @@ public final class JsonDecoder extends DefaultObjectPipe<String, StreamReceiver>
6264
private JsonParser jsonParser;
6365
private String arrayMarker = DEFAULT_ARRAY_MARKER;
6466
private String arrayName = DEFAULT_ARRAY_NAME;
67+
private String booleanMarker = DEFAULT_BOOLEAN_MARKER;
68+
private String numberMarker = DEFAULT_NUMBER_MARKER;
6569
private String recordId = DEFAULT_RECORD_ID;
6670
private String recordPath = DEFAULT_ROOT_PATH;
6771

@@ -109,6 +113,42 @@ public String getArrayMarker() {
109113
return arrayMarker;
110114
}
111115

116+
/**
117+
* Sets the boolean marker.
118+
*
119+
* @param booleanMarker the boolean marker
120+
*/
121+
public void setBooleanMarker(final String booleanMarker) {
122+
this.booleanMarker = booleanMarker;
123+
}
124+
125+
/**
126+
* Gets the boolean marker.
127+
*
128+
* @return the boolean marker
129+
*/
130+
public String getBooleanMarker() {
131+
return booleanMarker;
132+
}
133+
134+
/**
135+
* Sets the number marker.
136+
*
137+
* @param numberMarker the number marker
138+
*/
139+
public void setNumberMarker(final String numberMarker) {
140+
this.numberMarker = numberMarker;
141+
}
142+
143+
/**
144+
* Gets the number marker.
145+
*
146+
* @return the number marker
147+
*/
148+
public String getNumberMarker() {
149+
return numberMarker;
150+
}
151+
112152
/**
113153
* Sets the name of the array.
114154
*
@@ -290,14 +330,24 @@ private void decodeValue(final String name, final JsonToken token) throws IOExce
290330

291331
break;
292332
case START_ARRAY:
293-
getReceiver().startEntity(name + arrayMarker);
333+
getReceiver().startEntity(getMarkedName(name, arrayMarker));
294334
decodeArray();
295335
getReceiver().endEntity();
296336

297337
break;
298338
case VALUE_NULL:
299339
getReceiver().literal(name, null);
300340

341+
break;
342+
case VALUE_FALSE:
343+
case VALUE_TRUE:
344+
getReceiver().literal(getMarkedName(name, booleanMarker), jsonParser.getText());
345+
346+
break;
347+
case VALUE_NUMBER_FLOAT:
348+
case VALUE_NUMBER_INT:
349+
getReceiver().literal(getMarkedName(name, numberMarker), jsonParser.getText());
350+
301351
break;
302352
default:
303353
getReceiver().literal(name, jsonParser.getText());
@@ -306,4 +356,8 @@ private void decodeValue(final String name, final JsonToken token) throws IOExce
306356
}
307357
}
308358

359+
private String getMarkedName(final String name, final String marker) {
360+
return marker != null ? name + marker : name;
361+
}
362+
309363
}

metafacture-json/src/main/java/org/metafacture/json/JsonEncoder.java

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
public final class JsonEncoder extends DefaultStreamPipe<ObjectReceiver<String>> {
5454

5555
public static final String ARRAY_MARKER = "[]";
56+
public static final String BOOLEAN_MARKER = null;
57+
public static final String NUMBER_MARKER = null;
5658

5759
private static final char ESCAPE_CHAR_LOW = 0x20;
5860
private static final char ESCAPE_CHAR_HIGH = 0x7f;
@@ -61,6 +63,8 @@ public final class JsonEncoder extends DefaultStreamPipe<ObjectReceiver<String>>
6163
private final StringWriter writer = new StringWriter();
6264

6365
private String arrayMarker = ARRAY_MARKER;
66+
private String booleanMarker = BOOLEAN_MARKER;
67+
private String numberMarker = NUMBER_MARKER;
6468

6569
/**
6670
* Constructs a JsonEncoder if no IOException occurs. The root value
@@ -94,6 +98,42 @@ public String getArrayMarker() {
9498
return arrayMarker;
9599
}
96100

101+
/**
102+
* Sets the boolean marker.
103+
*
104+
* @param booleanMarker the boolean marker
105+
*/
106+
public void setBooleanMarker(final String booleanMarker) {
107+
this.booleanMarker = booleanMarker;
108+
}
109+
110+
/**
111+
* Gets the boolean marker.
112+
*
113+
* @return the boolean marker
114+
*/
115+
public String getBooleanMarker() {
116+
return booleanMarker;
117+
}
118+
119+
/**
120+
* Sets the number marker.
121+
*
122+
* @param numberMarker the number marker
123+
*/
124+
public void setNumberMarker(final String numberMarker) {
125+
this.numberMarker = numberMarker;
126+
}
127+
128+
/**
129+
* Gets the number marker.
130+
*
131+
* @return the number marker
132+
*/
133+
public String getNumberMarker() {
134+
return numberMarker;
135+
}
136+
97137
/**
98138
* Flags whether to use pretty printing.
99139
*
@@ -186,11 +226,18 @@ public void literal(final String name, final String value) {
186226
try {
187227
final JsonStreamContext ctx = jsonGenerator.getOutputContext();
188228
if (ctx.inObject()) {
189-
jsonGenerator.writeFieldName(name);
229+
jsonGenerator.writeFieldName(getUnmarkedName(name, booleanMarker, numberMarker));
190230
}
231+
191232
if (value == null) {
192233
jsonGenerator.writeNull();
193234
}
235+
else if (isMarkedName(name, booleanMarker)) {
236+
jsonGenerator.writeBoolean(Boolean.parseBoolean(value));
237+
}
238+
else if (isMarkedName(name, numberMarker)) {
239+
jsonGenerator.writeNumber(value);
240+
}
194241
else {
195242
jsonGenerator.writeString(value);
196243
}
@@ -206,9 +253,9 @@ public void literal(final String name, final String value) {
206253
private void startGroup(final String name) {
207254
try {
208255
final JsonStreamContext ctx = jsonGenerator.getOutputContext();
209-
if (name.endsWith(arrayMarker)) {
256+
if (isMarkedName(name, arrayMarker)) {
210257
if (ctx.inObject()) {
211-
jsonGenerator.writeFieldName(name.substring(0, name.length() - arrayMarker.length()));
258+
jsonGenerator.writeFieldName(getUnmarkedName(name, arrayMarker));
212259
}
213260
jsonGenerator.writeStartArray();
214261
}
@@ -245,6 +292,20 @@ else if (ctx.inArray()) {
245292
}
246293
}
247294

295+
private boolean isMarkedName(final String name, final String marker) {
296+
return marker != null && name.endsWith(marker);
297+
}
298+
299+
private String getUnmarkedName(final String name, final String... markers) {
300+
for (final String marker : markers) {
301+
if (isMarkedName(name, marker)) {
302+
return name.substring(0, name.length() - marker.length());
303+
}
304+
}
305+
306+
return name;
307+
}
308+
248309
private String escapeChar(final char ch) {
249310
final String namedEscape = namedEscape(ch);
250311
return namedEscape != null ? namedEscape : (ch < ESCAPE_CHAR_LOW || ESCAPE_CHAR_HIGH < ch) ? unicodeEscape(ch) : Character.toString(ch);

metafacture-json/src/test/java/org/metafacture/json/JsonDecoderTest.java

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,120 @@ public void testShouldProcessArrays() {
137137
ordered.verify(receiver).endRecord();
138138
}
139139

140+
@Test
141+
public void testShouldNotProcessBooleans() {
142+
jsonDecoder.process(
143+
"{" +
144+
"\"lit1\":false," +
145+
"\"lit2\":\"true\"," +
146+
"\"arr1\":[{\"lit3\":true,\"lit4\":\"false\"}]," +
147+
"\"arr2\":[false,true,\"false\"]" +
148+
"}"
149+
);
150+
151+
final InOrder ordered = inOrder(receiver);
152+
ordered.verify(receiver).startRecord("1");
153+
ordered.verify(receiver).literal("lit1", "false");
154+
ordered.verify(receiver).literal("lit2", "true");
155+
ordered.verify(receiver).startEntity("arr1[]");
156+
ordered.verify(receiver).startEntity("1");
157+
ordered.verify(receiver).literal("lit3", "true");
158+
ordered.verify(receiver).literal("lit4", "false");
159+
ordered.verify(receiver, times(2)).endEntity();
160+
ordered.verify(receiver).startEntity("arr2[]");
161+
ordered.verify(receiver).literal("1", "false");
162+
ordered.verify(receiver).literal("2", "true");
163+
ordered.verify(receiver).literal("3", "false");
164+
ordered.verify(receiver).endEntity();
165+
ordered.verify(receiver).endRecord();
166+
}
167+
168+
@Test
169+
public void testShouldProcessBooleansIfEnabled() {
170+
jsonDecoder.setBooleanMarker("~");
171+
jsonDecoder.process(
172+
"{" +
173+
"\"lit1\":false," +
174+
"\"lit2\":\"true\"," +
175+
"\"arr1\":[{\"lit3\":true,\"lit4\":\"false\"}]," +
176+
"\"arr2\":[false,true,\"false\"]" +
177+
"}"
178+
);
179+
180+
final InOrder ordered = inOrder(receiver);
181+
ordered.verify(receiver).startRecord("1");
182+
ordered.verify(receiver).literal("lit1~", "false");
183+
ordered.verify(receiver).literal("lit2", "true");
184+
ordered.verify(receiver).startEntity("arr1[]");
185+
ordered.verify(receiver).startEntity("1");
186+
ordered.verify(receiver).literal("lit3~", "true");
187+
ordered.verify(receiver).literal("lit4", "false");
188+
ordered.verify(receiver, times(2)).endEntity();
189+
ordered.verify(receiver).startEntity("arr2[]");
190+
ordered.verify(receiver).literal("1~", "false");
191+
ordered.verify(receiver).literal("2~", "true");
192+
ordered.verify(receiver).literal("3", "false");
193+
ordered.verify(receiver).endEntity();
194+
ordered.verify(receiver).endRecord();
195+
}
196+
197+
@Test
198+
public void testShouldNotProcessNumbers() {
199+
jsonDecoder.process(
200+
"{" +
201+
"\"lit1\":23," +
202+
"\"lit2\":\"4.2\"," +
203+
"\"arr1\":[{\"lit3\":4.2,\"lit4\":\"23\"}]," +
204+
"\"arr2\":[23,4.2,\"23\"]" +
205+
"}"
206+
);
207+
208+
final InOrder ordered = inOrder(receiver);
209+
ordered.verify(receiver).startRecord("1");
210+
ordered.verify(receiver).literal("lit1", "23");
211+
ordered.verify(receiver).literal("lit2", "4.2");
212+
ordered.verify(receiver).startEntity("arr1[]");
213+
ordered.verify(receiver).startEntity("1");
214+
ordered.verify(receiver).literal("lit3", "4.2");
215+
ordered.verify(receiver).literal("lit4", "23");
216+
ordered.verify(receiver, times(2)).endEntity();
217+
ordered.verify(receiver).startEntity("arr2[]");
218+
ordered.verify(receiver).literal("1", "23");
219+
ordered.verify(receiver).literal("2", "4.2");
220+
ordered.verify(receiver).literal("3", "23");
221+
ordered.verify(receiver).endEntity();
222+
ordered.verify(receiver).endRecord();
223+
}
224+
225+
@Test
226+
public void testShouldProcessNumbersIfEnabled() {
227+
jsonDecoder.setNumberMarker("#");
228+
jsonDecoder.process(
229+
"{" +
230+
"\"lit1\":23," +
231+
"\"lit2\":\"4.2\"," +
232+
"\"arr1\":[{\"lit3\":4.2,\"lit4\":\"23\"}]," +
233+
"\"arr2\":[23,4.2,\"23\"]" +
234+
"}"
235+
);
236+
237+
final InOrder ordered = inOrder(receiver);
238+
ordered.verify(receiver).startRecord("1");
239+
ordered.verify(receiver).literal("lit1#", "23");
240+
ordered.verify(receiver).literal("lit2", "4.2");
241+
ordered.verify(receiver).startEntity("arr1[]");
242+
ordered.verify(receiver).startEntity("1");
243+
ordered.verify(receiver).literal("lit3#", "4.2");
244+
ordered.verify(receiver).literal("lit4", "23");
245+
ordered.verify(receiver, times(2)).endEntity();
246+
ordered.verify(receiver).startEntity("arr2[]");
247+
ordered.verify(receiver).literal("1#", "23");
248+
ordered.verify(receiver).literal("2#", "4.2");
249+
ordered.verify(receiver).literal("3", "23");
250+
ordered.verify(receiver).endEntity();
251+
ordered.verify(receiver).endRecord();
252+
}
253+
140254
@Test
141255
public void testShouldProcessConcatenatedRecords() {
142256
jsonDecoder.process(

0 commit comments

Comments
 (0)