Skip to content

Commit cb77b49

Browse files
committed
Issue_66 Support for NaN, Inf and -Inf in JSON serialization
1 parent 418d7b1 commit cb77b49

File tree

6 files changed

+125
-11
lines changed

6 files changed

+125
-11
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ nbactions.xml
1515

1616
# For all you Mac OS users out there
1717
**/.DS_Store
18+
19+
# For all you IntelliJ users out there
20+
**/*.iml
21+
**/.idea

epics-vtype/vtype-json/src/main/java/org/epics/vtype/json/JsonArrays.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import javax.json.JsonArray;
1212
import javax.json.JsonArrayBuilder;
1313
import javax.json.JsonNumber;
14+
import javax.json.JsonObject;
1415
import javax.json.JsonString;
1516
import javax.json.JsonValue;
1617
import org.epics.util.array.ArrayByte;
@@ -82,9 +83,11 @@ public static boolean isStringArray(JsonArray array) {
8283
public static ListDouble toListDouble(JsonArray array) {
8384
double[] values = new double[array.size()];
8485
for (int i = 0; i < values.length; i++) {
85-
if (array.isNull(i)) {
86-
values[i] = Double.NaN;
87-
} else {
86+
Double doubleValue = VTypeToJsonV1.getDoubleFromJsonString(array.get(i).toString());
87+
if(doubleValue != null){
88+
values[i] = doubleValue;
89+
}
90+
else {
8891
values[i] = array.getJsonNumber(i).doubleValue();
8992
}
9093
}
@@ -313,9 +316,18 @@ public static JsonArrayBuilder fromListNumber(ListNumber list) {
313316
} else {
314317
for (int i = 0; i < list.size(); i++) {
315318
double value = list.getDouble(i);
316-
if (Double.isNaN(value) || Double.isInfinite(value)) {
317-
b.addNull();
318-
} else {
319+
if(Double.isFinite(value)){
320+
b.add(value);
321+
}
322+
else if (Double.isNaN(value)) {
323+
b.add(VTypeJsonMapper.NAN);
324+
} else if(Double.valueOf(value).equals(Double.POSITIVE_INFINITY)){
325+
b.add(VTypeJsonMapper.POS_INF);
326+
}
327+
else if(Double.valueOf(value).equals(Double.NEGATIVE_INFINITY)){
328+
b.add(VTypeJsonMapper.NEG_INF);
329+
}
330+
else {
319331
b.add(value);
320332
}
321333
}

epics-vtype/vtype-json/src/main/java/org/epics/vtype/json/JsonVTypeBuilder.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,26 @@ public JsonVTypeBuilder add(String string, long l) {
7070
return this;
7171
}
7272

73+
/**
74+
* For the special cases of {@link Double#NaN}, {@link Double#POSITIVE_INFINITY} and
75+
* {@link Double#NEGATIVE_INFINITY} this method adds a string representation of the value.
76+
* @param string Name of property to set.
77+
* @param d Value to set.
78+
* @return The {@link JsonVTypeBuilder} instance.
79+
*/
7380
@Override
7481
public JsonVTypeBuilder add(String string, double d) {
75-
if (Double.isNaN(d) || Double.isInfinite(d)) {
76-
builder.addNull(string);
77-
} else {
82+
if(Double.isFinite(d)){
7883
builder.add(string, d);
7984
}
85+
else if (Double.isNaN(d)) {
86+
builder.add(string, VTypeJsonMapper.NAN);
87+
} else if(Double.valueOf(d).equals(Double.POSITIVE_INFINITY)){
88+
builder.add(string, VTypeJsonMapper.POS_INF);
89+
}
90+
else if(Double.valueOf(d).equals(Double.NEGATIVE_INFINITY)){
91+
builder.add(string, VTypeJsonMapper.NEG_INF);
92+
}
8093
return this;
8194
}
8295

epics-vtype/vtype-json/src/main/java/org/epics/vtype/json/VTypeJsonMapper.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@
4141
* @author carcassi
4242
*/
4343
class VTypeJsonMapper implements JsonObject {
44+
45+
public static final String NAN = Double.toString(Double.NaN);
46+
/**
47+
* Quoted version of {@link Double#NaN}. Needed for comparison when de-serializing,
48+
* as serialization of {@link Double#NaN} creates a quotes string.
49+
*/
50+
public static final String NAN_QUOTED = "\"" + NAN + "\"";
51+
public static final String POS_INF = Double.toString(Double.POSITIVE_INFINITY);
52+
/**
53+
* Quoted version of {@link Double#POSITIVE_INFINITY}. Needed for comparison when de-serializing,
54+
* as serialization of {@link Double#POSITIVE_INFINITY} creates a quotes string.
55+
*/
56+
public static final String POS_INF_QUOTED = "\"" + POS_INF + "\"";
57+
public static final String NEG_INF = Double.toString(Double.NEGATIVE_INFINITY);
58+
/**
59+
* Quoted version of {@link Double#NEGATIVE_INFINITY}. Needed for comparison when de-serializing,
60+
* as serialization of {@link Double#NEGATIVE_INFINITY} creates a quotes string.
61+
*/
62+
public static final String NEG_INF_QUOTED = "\"" + NEG_INF + "\"";
4463

4564
private final JsonObject json;
4665

epics-vtype/vtype-json/src/main/java/org/epics/vtype/json/VTypeToJsonV1.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.epics.util.number.ULong;
1313
import org.epics.util.number.UShort;
1414
import org.epics.vtype.EnumDisplay;
15+
import org.epics.vtype.VDouble;
1516
import org.epics.vtype.VEnum;
1617
import org.epics.vtype.VNumber;
1718
import org.epics.vtype.VNumberArray;
@@ -78,11 +79,15 @@ static JsonObject toJson(VType vType) {
7879
throw new UnsupportedOperationException("Not implemented yet");
7980
}
8081

81-
static VNumber toVNumber(JsonObject json) {
82+
static VType toVNumber(JsonObject json) {
8283
VTypeJsonMapper mapper = new VTypeJsonMapper(json);
8384
Number value;
8485
switch(mapper.getTypeName()) {
8586
case "VDouble":
87+
Double doubleValue = getDoubleFromJsonString(json.get("value").toString());
88+
if(doubleValue != null){
89+
return VDouble.of(doubleValue, mapper.getAlarm(), mapper.getTime(), mapper.getDisplay());
90+
}
8691
value = mapper.getJsonNumber("value").doubleValue();
8792
break;
8893
case "VFloat":
@@ -207,4 +212,24 @@ static JsonObject toJson(VEnum vEnum) {
207212
.addEnum(vEnum)
208213
.build();
209214
}
215+
216+
/**
217+
* Converts an input (JSON) string to a {@link Double} for the special cases
218+
* {@link Double#NaN}, {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY}.
219+
* @param valueAsString
220+
* @return <code>null</code> if input is <code>null</code> or a valid double number, otherwise
221+
* {@link Double#NaN}, {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY}.
222+
*/
223+
public static Double getDoubleFromJsonString(String valueAsString){
224+
if(VTypeJsonMapper.NAN_QUOTED.equals(valueAsString)){
225+
return Double.NaN;
226+
}
227+
else if(VTypeJsonMapper.POS_INF_QUOTED.equals(valueAsString)){
228+
return Double.POSITIVE_INFINITY;
229+
}
230+
else if(VTypeJsonMapper.NEG_INF_QUOTED.equals(valueAsString)){
231+
return Double.NEGATIVE_INFINITY;
232+
}
233+
return null;
234+
}
210235
}

epics-vtype/vtype-json/src/test/java/org/epics/vtype/json/VTypeToJsonTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.time.Instant;
1212
import java.util.Arrays;
1313
import java.util.Collections;
14+
import java.util.List;
1415
import javax.json.Json;
1516
import javax.json.JsonObject;
1617
import javax.json.JsonReader;
@@ -288,5 +289,45 @@ public void vByteArray1() {
288289
testDeserialization("VByteArray1", vByteArray1);
289290
testDeserialization("VByteArray1a", vByteArray1);
290291
}
291-
292+
293+
/**
294+
* Tests serialization and de-serialization of a Double.NaN. See {@link JsonVTypeBuilder#add} and
295+
* {@link VTypeToJsonV1#toVNumber}
296+
*/
297+
@Test
298+
public void testDoubleNaN(){
299+
VDouble vDouble = VDouble.of(Double.NaN, Alarm.none(), Time.of(Instant.EPOCH), Display.none());
300+
JsonObject jsonObject = VTypeToJson.toJson(vDouble);
301+
assertEquals("NaN", jsonObject.getString("value"));
302+
vDouble = (VDouble)VTypeToJson.toVType(jsonObject);
303+
assertTrue(Double.isNaN(vDouble.getValue()));
304+
}
305+
306+
@Test
307+
public void testDoublePositiveInfinity(){
308+
VDouble vDouble = VDouble.of(Double.POSITIVE_INFINITY, Alarm.none(), Time.of(Instant.EPOCH), Display.none());
309+
JsonObject jsonObject = VTypeToJson.toJson(vDouble);
310+
assertEquals(Double.toString(Double.POSITIVE_INFINITY), jsonObject.getString("value"));
311+
vDouble = (VDouble)VTypeToJson.toVType(jsonObject);
312+
assertTrue(vDouble.getValue().equals(Double.POSITIVE_INFINITY));
313+
}
314+
315+
@Test
316+
public void testDoubleNegativeInfinity(){
317+
VDouble vDouble = VDouble.of(Double.NEGATIVE_INFINITY, Alarm.none(), Time.of(Instant.EPOCH), Display.none());
318+
JsonObject jsonObject = VTypeToJson.toJson(vDouble);
319+
assertEquals(VTypeJsonMapper.NEG_INF, jsonObject.getString("value"));
320+
vDouble = (VDouble)VTypeToJson.toVType(jsonObject);
321+
assertTrue(vDouble.getValue().equals(Double.NEGATIVE_INFINITY));
322+
}
323+
324+
@Test
325+
public void testDoubleNaNInArray(){
326+
VDoubleArray vDoubleArray1 = VDoubleArray.of(ArrayDouble.of(0, Double.NaN, 2), Alarm.none(), Time.of(Instant.ofEpochSecond(0, 0)), Display.none());
327+
JsonObject jsonObject = VTypeToJson.toJson(vDoubleArray1);
328+
List valueObject = (List)jsonObject.get("value");
329+
assertEquals(VTypeJsonMapper.NAN_QUOTED, valueObject.get(1).toString());
330+
vDoubleArray1 = (VDoubleArray)VTypeToJson.toVType(jsonObject);
331+
assertTrue(Double.isNaN(vDoubleArray1.getData().getDouble(1)));
332+
}
292333
}

0 commit comments

Comments
 (0)