Skip to content

Commit 828badf

Browse files
committed
Gson serialization/deserialization of VTable
1 parent 9875561 commit 828badf

4 files changed

Lines changed: 374 additions & 71 deletions

File tree

epics-vtype/vtype-gson/src/main/java/org/epics/vtype/gson/GsonArrays.java

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,12 @@
77
import com.google.gson.JsonArray;
88
import com.google.gson.JsonElement;
99
import com.google.gson.JsonNull;
10-
import org.epics.util.array.ArrayByte;
11-
import org.epics.util.array.ArrayDouble;
12-
import org.epics.util.array.ArrayFloat;
13-
import org.epics.util.array.ArrayInteger;
14-
import org.epics.util.array.ArrayLong;
15-
import org.epics.util.array.ArrayShort;
16-
import org.epics.util.array.ArrayUByte;
17-
import org.epics.util.array.ArrayUInteger;
18-
import org.epics.util.array.ArrayULong;
19-
import org.epics.util.array.ArrayUShort;
20-
import org.epics.util.array.ListByte;
21-
import org.epics.util.array.ListDouble;
22-
import org.epics.util.array.ListFloat;
23-
import org.epics.util.array.ListInteger;
24-
import org.epics.util.array.ListLong;
25-
import org.epics.util.array.ListNumber;
26-
import org.epics.util.array.ListShort;
27-
import org.epics.util.array.ListUByte;
28-
import org.epics.util.array.ListUInteger;
29-
import org.epics.util.array.ListULong;
30-
import org.epics.util.array.ListUShort;
10+
import org.epics.util.array.*;
3111
import org.epics.util.number.UnsignedConversions;
3212

3313
import java.time.Instant;
3414
import java.util.ArrayList;
15+
import java.util.Collections;
3516
import java.util.List;
3617

3718
/**
@@ -220,6 +201,21 @@ public static ListByte toListByte(JsonArray array) {
220201
return ArrayByte.of(values);
221202
}
222203

204+
/**
205+
* Converts the given numeric JSON array to a ListBoolean.
206+
*
207+
* @param array an array of booleans
208+
* @return a new ListBoolean
209+
*/
210+
public static ListBoolean toListBoolean(JsonArray array) {
211+
boolean[] values = new boolean[array.size()];
212+
for (int i = 0; i < values.length; i++) {
213+
values[i] = array.get(i).getAsBoolean();
214+
}
215+
return ArrayBoolean.of(values);
216+
}
217+
218+
223219
/**
224220
* Converts the given string JSON array to a List of Strings.
225221
*
@@ -327,5 +323,30 @@ else if(Double.valueOf(value).equals(Double.NEGATIVE_INFINITY)){
327323
}
328324
return b;
329325
}
326+
327+
public static JsonArray fromList(Object object){
328+
if(object instanceof ListNumber){
329+
return fromListNumber((ListNumber) object);
330+
}
331+
else if(object instanceof ListBoolean){
332+
return fromListBoolean((ListBoolean) object);
333+
}
334+
else if (object instanceof List) {
335+
List list = (List) object;
336+
if (list.isEmpty()) {
337+
return fromListString(Collections.emptyList());
338+
}
339+
return fromListString(list);
340+
}
341+
return new JsonArray();
342+
}
343+
344+
private static JsonArray fromListBoolean(ListBoolean listBoolean){
345+
JsonArray builder = new JsonArray();
346+
for(int i = 0; i < listBoolean.size(); i++){
347+
builder.add(listBoolean.getBoolean(i));
348+
}
349+
return builder;
350+
}
330351

331352
}

epics-vtype/vtype-gson/src/main/java/org/epics/vtype/gson/VTypeToGson.java

Lines changed: 190 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,18 @@
44
*/
55
package org.epics.vtype.gson;
66

7+
import com.google.gson.JsonArray;
78
import com.google.gson.JsonElement;
8-
import org.epics.util.array.ListNumber;
9+
import com.google.gson.JsonObject;
10+
import org.epics.util.array.*;
911
import org.epics.util.number.UByte;
1012
import org.epics.util.number.UInteger;
1113
import org.epics.util.number.ULong;
1214
import org.epics.util.number.UShort;
13-
import org.epics.vtype.EnumDisplay;
14-
import org.epics.vtype.VDouble;
15-
import org.epics.vtype.VEnum;
16-
import org.epics.vtype.VNumber;
17-
import org.epics.vtype.VNumberArray;
18-
import org.epics.vtype.VString;
19-
import org.epics.vtype.VStringArray;
20-
import org.epics.vtype.VType;
15+
import org.epics.vtype.*;
2116

17+
import java.util.ArrayList;
18+
import java.util.Collections;
2219
import java.util.List;
2320

2421
/**
@@ -61,6 +58,8 @@ static VType toVType(JsonElement json) {
6158
return toVStringArray(json);
6259
case "VEnum":
6360
return toVEnum(json);
61+
case "VTable":
62+
return toVTable(json);
6463
default:
6564
throw new UnsupportedOperationException("Not implemented yet");
6665
}
@@ -85,6 +84,8 @@ static JsonElement toJson(VType vType) {
8584
return toJson((VStringArray) vType);
8685
} else if (vType instanceof VEnum) {
8786
return toJson((VEnum) vType);
87+
} else if(vType instanceof VTable) {
88+
return toJson((VTable) vType);
8889
}
8990
throw new UnsupportedOperationException("Not implemented yet");
9091
}
@@ -261,4 +262,184 @@ else if(VTypeGsonMapper.NEG_INF_QUOTED.equals(valueAsString)){
261262
return Double.parseDouble(valueAsString);
262263
}
263264
}
265+
266+
/**
267+
* Deserializes the Gson representation of a {@link VTable}
268+
* @param jsonElement JSON representation
269+
* @return A {@link VTable} object.
270+
*/
271+
static VTable toVTable(JsonElement jsonElement) {
272+
JsonObject jsonObject = jsonElement.getAsJsonObject();
273+
int columnCount = jsonObject.get("columnCount").getAsInt();
274+
List<String> listString = new ArrayList<>();
275+
List<Class<?>> listClass = new ArrayList<>();
276+
JsonArray columnNames = jsonObject.get("columnNames").getAsJsonArray();
277+
JsonArray columnTypes = jsonObject.get("columnTypes").getAsJsonArray();
278+
List<Object> listObject = new ArrayList<>();
279+
for (int i = 0; i < columnCount; i++) {
280+
listString.add(i, columnNames.get(i).getAsString());
281+
Class<?> clazz = getClass(columnTypes.get(i).getAsString());
282+
listClass.add(i, clazz);
283+
JsonArray value = jsonObject.get("value").getAsJsonArray();
284+
JsonArray ja = value.get(i).getAsJsonArray();
285+
clazz = listClass.get(i);
286+
if (ja == null || ja.size() == 0 || clazz == null) {
287+
listObject.add(i, Collections.emptyList());
288+
} else {
289+
String columnType = columnTypes.get(i).getAsString();
290+
switch (columnType){
291+
case "bool":
292+
listObject.add(i, GsonArrays.toListBoolean(ja));
293+
break;
294+
case "byte":
295+
listObject.add(i, GsonArrays.toListByte(ja));
296+
break;
297+
case "ubyte":
298+
listObject.add(i, GsonArrays.toListUByte(ja));
299+
break;
300+
case "short":
301+
listObject.add(i, GsonArrays.toListShort(ja));
302+
break;
303+
case "ushort":
304+
listObject.add(i, GsonArrays.toListUShort(ja));
305+
break;
306+
case "int":
307+
listObject.add(i, GsonArrays.toListInt(ja));
308+
break;
309+
case "uint":
310+
listObject.add(i, GsonArrays.toListUInteger(ja));
311+
break;
312+
case "long":
313+
listObject.add(i, GsonArrays.toListLong(ja));
314+
break;
315+
case "ulong":
316+
listObject.add(i, GsonArrays.toListULong(ja));
317+
break;
318+
case "float":
319+
listObject.add(i, GsonArrays.toListFloat(ja));
320+
break;
321+
case "double":
322+
listObject.add(i, GsonArrays.toListDouble(ja));
323+
break;
324+
case "string":
325+
listObject.add(i, GsonArrays.toListString(ja));
326+
break;
327+
default:
328+
listObject.add(i, Collections.emptyList());
329+
}
330+
}
331+
}
332+
return VTable.of(listClass, listString, listObject);
333+
}
334+
335+
/**
336+
* @param name A type name determined at serialization time. The &quot;unsigned primitives&quot; (ushort, uint...)
337+
* are allowed.
338+
* @return A {@link Class} derived from the name, where - for instance - int and uint both map to
339+
* {@link Integer#TYPE}. For unsupported type names <code>null</code> is returned.
340+
*/
341+
static Class<?> getClass(String name) {
342+
switch (name) {
343+
case "bool":
344+
return Boolean.TYPE;
345+
case "int":
346+
case "uint":
347+
return Integer.TYPE;
348+
case "long":
349+
case "ulong":
350+
return Long.TYPE;
351+
case "short":
352+
case "ushort":
353+
return Short.TYPE;
354+
case "byte":
355+
case "ubyte":
356+
return Byte.TYPE;
357+
case "double":
358+
return Double.TYPE;
359+
case "float":
360+
return Float.TYPE;
361+
case "string":
362+
return String.class;
363+
default:
364+
return null;
365+
}
366+
}
367+
368+
/**
369+
* Serializes a {@link VTable} object, though with the restriction that data is
370+
* structured as arrays, but not any other nested ot complex structure, e.g. arrays of arrays.
371+
* The array data may be of different types, but EPICS requires data columns to be of equal length.
372+
* @param vTable A {@link VTable} object
373+
* @return Gson representation.
374+
*/
375+
static JsonObject toJson(VTable vTable) {
376+
GsonVTypeBuilder jsonVTypeBuilder = new GsonVTypeBuilder();
377+
int columnCount = vTable.getColumnCount();
378+
List<String> columnNames = new ArrayList<>();
379+
List<String> columnTypes = new ArrayList<>();
380+
for (int i = 0; i < columnCount; i++) {
381+
columnNames.add(i, vTable.getColumnName(i));
382+
columnTypes.add(i, getVTableDataType(vTable.getColumnData(i)));
383+
}
384+
jsonVTypeBuilder.addType(vTable)
385+
.add("columnCount", columnCount)
386+
.addListString("columnNames", columnNames);
387+
388+
jsonVTypeBuilder.addListString("columnTypes", columnTypes);
389+
JsonArray valueBuilder = new JsonArray();
390+
for (int i = 0; i < columnCount; i++) {
391+
valueBuilder.add(GsonArrays.fromList(vTable.getColumnData(i)));
392+
}
393+
jsonVTypeBuilder.add("value", valueBuilder);
394+
return jsonVTypeBuilder.build();
395+
}
396+
397+
/**
398+
* An NTTable's value columns are always of type struct_t[]. In order to be able to preserve
399+
* the type information when serializing, this method returns a string representation
400+
* of the array type.
401+
* @param data NTTable value field that is always of array type.
402+
* @return A string representation of the array type.
403+
*/
404+
static String getVTableDataType(Object data){
405+
if(data instanceof ArrayBoolean){
406+
return "bool";
407+
}
408+
else if(data instanceof ArrayByte){
409+
return "byte";
410+
}
411+
else if(data instanceof ArrayUByte){
412+
return "ubyte";
413+
}
414+
else if(data instanceof ArrayShort){
415+
return "short";
416+
}
417+
else if(data instanceof ArrayUShort){
418+
return "ushort";
419+
}
420+
else if (data instanceof ArrayInteger){
421+
return "int";
422+
}
423+
else if(data instanceof ArrayUInteger){
424+
return "uint";
425+
}
426+
else if(data instanceof ArrayLong){
427+
return "long";
428+
}
429+
else if(data instanceof ArrayULong){
430+
return "ulong";
431+
}
432+
else if(data instanceof ArrayFloat){
433+
return "float";
434+
}
435+
else if(data instanceof ArrayDouble){
436+
return "double";
437+
}
438+
else if(data instanceof List){
439+
return "string";
440+
}
441+
else{
442+
return "invalid";
443+
}
444+
}
264445
}

0 commit comments

Comments
 (0)