diff --git a/pom.xml b/pom.xml
index 5f114aed34f..5da40049817 100644
--- a/pom.xml
+++ b/pom.xml
@@ -250,7 +250,7 @@
**/org/rascalmpl/test/library/LibraryLangPaths.java
**/org/rascalmpl/test/value/AllTests.java
**/org/rascalmpl/test/repl/*Test.java
- **/org/rascalmpl/test/rpc/*Test.java
+ **/org/rascalmpl/test/rpc/*Tests.java
**/org/rascalmpl/util/**/*Test.java
**/org/rascalmpl/util/**/*Tests.java
**/org/rascalmpl/uri/**/*Test.java
diff --git a/src/org/rascalmpl/ideservices/GsonUtils.java b/src/org/rascalmpl/ideservices/GsonUtils.java
index fdf0d273f51..e9b2ee320c9 100644
--- a/src/org/rascalmpl/ideservices/GsonUtils.java
+++ b/src/org/rascalmpl/ideservices/GsonUtils.java
@@ -27,15 +27,16 @@
package org.rascalmpl.ideservices;
import java.io.IOException;
-import java.io.StringWriter;
+import java.io.StringReader;
import java.util.List;
+import java.util.function.Consumer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.rascalmpl.interpreter.NullRascalMonitor;
import org.rascalmpl.library.lang.json.internal.JsonValueReader;
import org.rascalmpl.library.lang.json.internal.JsonValueWriter;
import org.rascalmpl.util.base64.StreamingBase64;
-import org.rascalmpl.values.IRascalValueFactory;
+import org.rascalmpl.values.ValueFactoryFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -59,6 +60,8 @@
import io.usethesource.vallang.IString;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
+import io.usethesource.vallang.IValueFactory;
+import io.usethesource.vallang.io.StandardTextReader;
import io.usethesource.vallang.io.binary.stream.IValueInputStream;
import io.usethesource.vallang.io.binary.stream.IValueOutputStream;
import io.usethesource.vallang.type.Type;
@@ -71,8 +74,8 @@
*/
public class GsonUtils {
private static final JsonValueWriter writer = new JsonValueWriter();
- private static final JsonValueReader reader = new JsonValueReader(IRascalValueFactory.getInstance(), new TypeStore(), new NullRascalMonitor(), null);
private static final TypeFactory tf = TypeFactory.getInstance();
+ private static final IValueFactory vf = ValueFactoryFactory.getValueFactory();
private static final List typeMappings;
@@ -80,7 +83,7 @@ public class GsonUtils {
* Subtypes should be declared before their supertypes; e.g., `Number` and `Value` appear last.
*/
static {
- writer.setDatesAsInt(true);
+ writer.setRationalsAsString(true);
typeMappings = List.of(
new TypeMapping(IBool.class, tf.boolType()),
new TypeMapping(ICollection.class), // IList, IMap, ISet
@@ -100,9 +103,26 @@ public class GsonUtils {
}
public static enum ComplexTypeMode {
+ /**
+ * Complex values are serialized as JSON objects. Automatic deserialization is only supported for primitive types (`bool`,
+ * `datetime`, `int`, `rat`, `real`, `loc`, `str`, `num`); more complex types cannot be automatically deserialized as
+ * the type is not available at deserialization time.
+ */
ENCODE_AS_JSON_OBJECT,
+
+ /**
+ * Complex values are serialized as a Base64-encoded string. A properly filled {@link TypeStore} is required for deserialization.
+ */
ENCODE_AS_BASE64_STRING,
+
+ /**
+ * Complex values are serialized as a string. A properly filled {@link TypeStore} is required for deserialization.
+ */
ENCODE_AS_STRING,
+
+ /**
+ * Only values of primitive type are supported; more complex values are neither serialized nor deserialized.
+ */
NOT_SUPPORTED
}
@@ -129,35 +149,19 @@ public boolean supports(Class> incoming) {
return clazz.isAssignableFrom(incoming);
}
- public TypeAdapter createAdapter(ComplexTypeMode complexTypeMode) {
+ public TypeAdapter createAdapter(ComplexTypeMode complexTypeMode, TypeStore ts) {
+ var reader = new JsonValueReader(vf, ts, new NullRascalMonitor(), null);
if (isPrimitive) {
return new TypeAdapter() {
@Override
public void write(JsonWriter out, T value) throws IOException {
- var needsWrapping = needsWrapping(type, complexTypeMode);
- if (needsWrapping) {
- out.beginObject();
- out.name("val");
- }
writer.write(out, (IValue) value);
- if (needsWrapping) {
- out.endObject();
- }
}
@SuppressWarnings("unchecked")
@Override
public T read(JsonReader in) throws IOException {
- var needsWrapping = needsWrapping(type, complexTypeMode);
- if (needsWrapping) {
- in.beginObject();
- in.nextName();
- }
- var ret = (T) reader.read(in, type);
- if (needsWrapping) {
- in.endObject();
- }
- return ret;
+ return (T) reader.read(in, type);
}
};
}
@@ -166,15 +170,7 @@ public T read(JsonReader in) throws IOException {
public void write(JsonWriter out, T value) throws IOException {
switch (complexTypeMode) {
case ENCODE_AS_JSON_OBJECT:
- var needsWrapping = needsWrapping(type, complexTypeMode);
- if (needsWrapping) {
- out.beginObject();
- out.name("val");
- }
writer.write(out, (IValue) value);
- if (needsWrapping) {
- out.endObject();
- }
break;
case ENCODE_AS_BASE64_STRING:
out.value(base64Encode((IValue) value));
@@ -189,29 +185,81 @@ public void write(JsonWriter out, T value) throws IOException {
}
}
+ @SuppressWarnings("unchecked")
@Override
public T read(JsonReader in) throws IOException {
- throw new IOException("Cannot handle complex type");
+ switch (complexTypeMode) {
+ case ENCODE_AS_BASE64_STRING:
+ return base64Decode(in.nextString(), ts);
+ case ENCODE_AS_STRING:
+ return (T) new StandardTextReader().read(vf, ts, tf.valueType(), new StringReader(in.nextString()));
+ default:
+ throw new IOException("Cannot handle complex type");
+ }
}
};
}
}
-
+
+ /**
+ * Configure Gson to encode complex (non-primitive) values as JSON objects.
+ *
+ * See {@link ComplexTypeMode.ENCODE_AS_JSON_OBJECT}.
+ */
+ public static Consumer complexAsJsonObject() {
+ return builder -> configureGson(builder, ComplexTypeMode.ENCODE_AS_JSON_OBJECT, new TypeStore());
+ }
+
/**
- * IValues that are encoded as a (JSON) list need to be wrapped in an object to avoid Gson accidentally unpacking the list
- * @param type
- * @param complexTypeMode
- * @return whether or not wrapping is required
+ * Configure Gson to encode complex (non-primitive) values as Base64-encoded strings.
+ *
+ * This configurtion should only be used for serialization; deserialization requires a {@link TypeStore).
*/
- private static boolean needsWrapping(Type type, ComplexTypeMode complexTypeMode) {
- return complexTypeMode == ComplexTypeMode.ENCODE_AS_JSON_OBJECT && type == null || type.isSubtypeOf(tf.rationalType());
+ public static Consumer complexAsBase64String() {
+ return builder -> complexAsBase64String(new TypeStore());
}
- public static void configureGson(GsonBuilder builder) {
- configureGson(builder, ComplexTypeMode.ENCODE_AS_JSON_OBJECT);
+ /**
+ * Configure Gson to encode complex (non-primitive) values as Base64-encoded strings.
+ *
+ * This configuration can be used for both serialization and deserialization.
+ *
+ * @param ts The {@link TypeStore} to be used during deserialization.
+ */
+ public static Consumer complexAsBase64String(TypeStore ts) {
+ return builder -> configureGson(builder, ComplexTypeMode.ENCODE_AS_BASE64_STRING, ts);
+ }
+
+ /**
+ * Configure Gson to encode complex (non-primitive) values as plain strings.
+ *
+ * This configurtion should only be used for serialization; deserialization requires a {@link TypeStore).
+ */
+ public static Consumer complexAsString() {
+ return builder -> complexAsString(new TypeStore());
+ }
+
+ /**
+ * Configure Gson to encode complex (non-primitive) values as plain strings.
+ *
+ * This configuration can be used for both serialization and deserialization.
+ *
+ * @param ts The {@link TypeStore} to be used during deserialization.
+ */
+ public static Consumer complexAsString(TypeStore ts) {
+ return builder -> configureGson(builder, ComplexTypeMode.ENCODE_AS_STRING, ts);
+ }
+
+ /**
+ * Configure Gson to encode encode primitive values only. Complex values raise an exception.
+ *
+ * @param builder The {@link GsonBuilder} to be configured.
+ */
+ public static Consumer noComplexTypes() {
+ return builder -> configureGson(builder, ComplexTypeMode.NOT_SUPPORTED, new TypeStore());
}
- public static void configureGson(GsonBuilder builder, ComplexTypeMode complexTypeMode) {
+ public static void configureGson(GsonBuilder builder, ComplexTypeMode complexTypeMode, TypeStore ts) {
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public TypeAdapter create(Gson gson, TypeToken type) {
@@ -222,7 +270,7 @@ public TypeAdapter create(Gson gson, TypeToken type) {
return typeMappings.stream()
.filter(m -> m.supports(rawType))
.findFirst()
- .map(m -> m.createAdapter(complexTypeMode))
+ .map(m -> m.createAdapter(complexTypeMode, ts))
.orElse(null);
}
});
@@ -231,7 +279,7 @@ public TypeAdapter create(Gson gson, TypeToken type) {
public static String base64Encode(IValue value) {
var builder = new StringBuilder();
try (var encoder = StreamingBase64.encode(builder);
- var out = new IValueOutputStream(encoder, IRascalValueFactory.getInstance())) {
+ var out = new IValueOutputStream(encoder, vf)) {
out.write(value);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -242,7 +290,7 @@ public static String base64Encode(IValue value) {
@SuppressWarnings("unchecked")
public static T base64Decode(String string, TypeStore ts) {
try (var decoder = StreamingBase64.decode(string);
- var in = new IValueInputStream(decoder, IRascalValueFactory.getInstance(), () -> ts)) {
+ var in = new IValueInputStream(decoder, vf, () -> ts)) {
return (T) in.read();
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/src/org/rascalmpl/ideservices/RemoteIDEServices.java b/src/org/rascalmpl/ideservices/RemoteIDEServices.java
index cbfc44eacae..14af1ba9941 100644
--- a/src/org/rascalmpl/ideservices/RemoteIDEServices.java
+++ b/src/org/rascalmpl/ideservices/RemoteIDEServices.java
@@ -64,7 +64,7 @@ public RemoteIDEServices(int ideServicesPort, PrintWriter stderr, IRascalMonitor
.setLocalService(this)
.setInput(socket.getInputStream())
.setOutput(socket.getOutputStream())
- .configureGson(GsonUtils::configureGson)
+ .configureGson(GsonUtils.complexAsJsonObject())
.setExecutorService(DaemonThreadPool.buildConstrainedCached("rascal-ide-services", Math.max(2, Math.min(6, Runtime.getRuntime().availableProcessors() - 2))))
.create();
diff --git a/src/org/rascalmpl/library/Content.rsc b/src/org/rascalmpl/library/Content.rsc
index bc3c95f09f7..f7615a63368 100644
--- a/src/org/rascalmpl/library/Content.rsc
+++ b/src/org/rascalmpl/library/Content.rsc
@@ -81,7 +81,8 @@ which involves a handy, automatic, encoding of Rascal values into json values.
data Response
= response(Status status, str mimeType, map[str,str] header, str content)
| fileResponse(loc file, str mimeType, map[str,str] header)
- | jsonResponse(Status status, map[str,str] header, value val, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", JSONFormatter[value] formatter = str (value _) { fail; }, bool explicitConstructorNames=false, bool explicitDataTypes=false)
+ | jsonResponse(Status status, map[str,str] header, value val, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", JSONFormatter[value] formatter = str (value _) { fail; },
+ bool explicitConstructorNames=false, bool explicitDataTypes=false, bool dateTimeAsInt=true, bool rationalsAsString=false)
;
@synopsis{Utility to quickly render a string as HTML content}
diff --git a/src/org/rascalmpl/library/lang/json/IO.java b/src/org/rascalmpl/library/lang/json/IO.java
index 90498e96690..55a2b12b160 100644
--- a/src/org/rascalmpl/library/lang/json/IO.java
+++ b/src/org/rascalmpl/library/lang/json/IO.java
@@ -111,7 +111,7 @@ public IValue parseJSON(IValue type, IString src, IString dateTimeFormat, IBool
}
public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations, IString dateTimeFormat,
- IBool dateTimeAsInt, IInteger indent, IBool dropOrigins, IFunction formatter, IBool explicitConstructorNames,
+ IBool dateTimeAsInt, IBool rationalsAsString, IInteger indent, IBool dropOrigins, IFunction formatter, IBool explicitConstructorNames,
IBool explicitDataTypes) {
try (JsonWriter out =
new JsonWriter(new OutputStreamWriter(URIResolverRegistry.getInstance().getOutputStream(loc, false),
@@ -123,6 +123,7 @@ public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations
new JsonValueWriter()
.setCalendarFormat(dateTimeFormat.getValue())
.setDatesAsInt(dateTimeAsInt.getValue())
+ .setRationalsAsString(rationalsAsString.getValue())
.setUnpackedLocations(unpackedLocations.getValue())
.setDropOrigins(dropOrigins.getValue())
.setFormatters(formatter)
@@ -135,7 +136,7 @@ public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations
}
}
- public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt,
+ public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt, IBool rationalsAsString,
IInteger indent, IBool dropOrigins, IFunction formatter, IBool explicitConstructorNames,
IBool explicitDataTypes) {
StringWriter string = new StringWriter();
@@ -147,6 +148,7 @@ public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFor
new JsonValueWriter()
.setCalendarFormat(dateTimeFormat.getValue())
.setDatesAsInt(dateTimeAsInt.getValue())
+ .setRationalsAsString(rationalsAsString.getValue())
.setUnpackedLocations(unpackedLocations.getValue())
.setDropOrigins(dropOrigins.getValue())
.setFormatters(formatter)
diff --git a/src/org/rascalmpl/library/lang/json/IO.rsc b/src/org/rascalmpl/library/lang/json/IO.rsc
index 0a3589b1201..8c71c94ed72 100644
--- a/src/org/rascalmpl/library/lang/json/IO.rsc
+++ b/src/org/rascalmpl/library/lang/json/IO.rsc
@@ -162,7 +162,8 @@ For `real` numbers that are larger than JVM's double you get "negative infinity"
java void writeJSON(loc target, value val,
bool unpackedLocations=false,
str dateTimeFormat=DEFAULT_DATETIME_FORMAT,
- bool dateTimeAsInt=false,
+ bool dateTimeAsInt=false,
+ bool rationalsAsString=false,
int indent=0,
bool dropOrigins=true,
JSONFormatter[value] formatter = str (value _) { fail; },
@@ -176,7 +177,7 @@ java void writeJSON(loc target, value val,
@description{
This function uses `writeJSON` and stores the result in a string.
}
-java str asJSON(value val, bool unpackedLocations=false, str dateTimeFormat=DEFAULT_DATETIME_FORMAT, bool dateTimeAsInt=false, int indent = 0, bool dropOrigins=true, JSONFormatter[value] formatter = str (value _) { fail; }, bool explicitConstructorNames=false, bool explicitDataTypes=false);
+java str asJSON(value val, bool unpackedLocations=false, str dateTimeFormat=DEFAULT_DATETIME_FORMAT, bool dateTimeAsInt=false, bool rationalsAsString=false, int indent = 0, bool dropOrigins=true, JSONFormatter[value] formatter = str (value _) { fail; }, bool explicitConstructorNames=false, bool explicitDataTypes=false);
@synopsis{((writeJSON)) and ((asJSON)) uses `Formatter` functions to flatten structured data to strings, on-demand}
@description{
diff --git a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java
index f33a87762f3..036d78c1c0c 100644
--- a/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java
+++ b/src/org/rascalmpl/library/lang/json/internal/JsonValueReader.java
@@ -376,7 +376,7 @@ public IValue visitRational(Type type) throws IOException {
case STRING:
return vf.rational(nextString());
default:
- throw parseErrorHere("Expected integer but got " + in.peek());
+ throw parseErrorHere("Expected rational but got " + in.peek());
}
}
diff --git a/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java b/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java
index 02c91578e0d..ad7e3eea891 100644
--- a/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java
+++ b/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java
@@ -61,6 +61,7 @@
public class JsonValueWriter {
private ThreadLocal format;
private boolean datesAsInts = true;
+ private boolean rationalsAsString = false;
private boolean unpackedLocations = false;
private boolean dropOrigins = true;
private IFunction formatters;
@@ -127,6 +128,11 @@ public JsonValueWriter setDatesAsInt(boolean setting) {
return this;
}
+ public JsonValueWriter setRationalsAsString(boolean setting) {
+ this.rationalsAsString = setting;
+ return this;
+ }
+
public JsonValueWriter setUnpackedLocations(boolean setting) {
this.unpackedLocations = setting;
return this;
@@ -175,10 +181,14 @@ public Void visitReal(IReal o) throws IOException {
@Override
public Void visitRational(IRational o) throws IOException {
- out.beginArray();
- o.numerator().accept(this);
- o.denominator().accept(this);
- out.endArray();
+ if (rationalsAsString) {
+ out.value(o.toString());
+ } else {
+ out.beginArray();
+ o.numerator().accept(this);
+ o.denominator().accept(this);
+ out.endArray();
+ }
return null;
}
diff --git a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
index 1c889cb2baf..8050794adda 100644
--- a/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
+++ b/src/org/rascalmpl/library/lang/rascal/tests/library/lang/json/JSONIOTests.rsc
@@ -14,8 +14,8 @@ import Node;
loc targetFile = |memory://test-tmp/test-<"">.json|;
public int maxLong = floor(pow(2,63));
-bool writeRead(type[&T] returnType, &T dt, value (value x) normalizer = value(value x) { return x; }, bool dateTimeAsInt=false, bool unpackedLocations=false, bool explicitConstructorNames=false, bool explicitDataTypes=false) {
- json = asJSON(dt, dateTimeAsInt=dateTimeAsInt, unpackedLocations=unpackedLocations, explicitConstructorNames=explicitConstructorNames, explicitDataTypes=explicitDataTypes);
+bool writeRead(type[&T] returnType, &T dt, value (value x) normalizer = value(value x) { return x; }, bool dateTimeAsInt=false, bool rationalsAsString=false, bool unpackedLocations=false, bool explicitConstructorNames=false, bool explicitDataTypes=false) {
+ json = asJSON(dt, dateTimeAsInt=dateTimeAsInt, rationalsAsString=rationalsAsString, unpackedLocations=unpackedLocations, explicitConstructorNames=explicitConstructorNames, explicitDataTypes=explicitDataTypes);
readBack = normalizer(parseJSON(returnType, json, explicitConstructorNames=explicitConstructorNames, explicitDataTypes=explicitDataTypes));
if (readBack !:= normalizer(dt) /* ignores additional src fields */) {
println("What is read back, a :");
@@ -67,6 +67,8 @@ test bool jsonWithSet1(set[int] dt) = writeRead(#set[int], dt);
test bool jsonWithMap1(map[int, int] dt) = writeRead(#map[int,int], dt);
@ignore{until #2133 is fixed}
test bool jsonWithNode1(node dt) = writeRead(#node, dt, normalizer = toDefaultRec);
+test bool jsonWithRational1(rat r) = writeRead(#rat, r);
+test bool jsonWithRational2(rat r) = writeRead(#rat, r, rationalsAsString=true);
test bool jsonWithDATA11(DATA1 dt) = writeRead(#DATA1, dt);
test bool jsonWithDATA21(DATA2 dt) = writeRead(#DATA2, dt);
diff --git a/src/org/rascalmpl/library/util/TermREPL.java b/src/org/rascalmpl/library/util/TermREPL.java
index 5cdc2e5c3d9..a98798b79ea 100644
--- a/src/org/rascalmpl/library/util/TermREPL.java
+++ b/src/org/rascalmpl/library/util/TermREPL.java
@@ -282,6 +282,7 @@ private ICommandOutput handleJSONResponse(IConstructor response) {
IValue dtf = kws.getParameter("dateTimeFormat");
IValue dai = kws.getParameter("dateTimeAsInt");
+ IValue ras = kws.getParameter("rationalsAsString");
IValue formatters = kws.getParameter("formatter");
IValue ecn = kws.getParameter("explicitConstructorNames");
IValue edt = kws.getParameter("explicitDataTypes");
@@ -290,6 +291,7 @@ private ICommandOutput handleJSONResponse(IConstructor response) {
.setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'")
.setFormatters((IFunction) formatters)
.setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true)
+ .setRationalsAsString(ras != null ? ((IBool) ras).getValue() : false)
.setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false)
.setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false)
;
diff --git a/src/org/rascalmpl/library/util/Webserver.java b/src/org/rascalmpl/library/util/Webserver.java
index 5d6ab6fb24e..5dd103a0b5e 100644
--- a/src/org/rascalmpl/library/util/Webserver.java
+++ b/src/org/rascalmpl/library/util/Webserver.java
@@ -241,6 +241,7 @@ private Response translateJsonResponse(Method method, IConstructor cons) {
IValue dtf = kws.getParameter("dateTimeFormat");
IValue dai = kws.getParameter("dateTimeAsInt");
+ IValue ras = kws.getParameter("rationalsAsString");
IValue formatters = kws.getParameter("formatter");
IValue ecn = kws.getParameter("explicitConstructorNames");
IValue edt = kws.getParameter("explicitDataTypes");
@@ -249,6 +250,7 @@ private Response translateJsonResponse(Method method, IConstructor cons) {
.setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'")
.setFormatters((IFunction) formatters)
.setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true)
+ .setRationalsAsString(ras != null ? ((IBool) ras).getValue() : false)
.setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false)
.setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false)
;
diff --git a/src/org/rascalmpl/repl/http/REPLContentServer.java b/src/org/rascalmpl/repl/http/REPLContentServer.java
index 9fd5de65d73..498517f36ba 100644
--- a/src/org/rascalmpl/repl/http/REPLContentServer.java
+++ b/src/org/rascalmpl/repl/http/REPLContentServer.java
@@ -163,6 +163,7 @@ private static Response translateJsonResponse(Method method, IConstructor cons)
IValue dtf = kws.getParameter("dateTimeFormat");
IValue dai = kws.getParameter("dateTimeAsInt");
+ IValue ras = kws.getParameter("rationalsAsString");
IValue formatters = kws.getParameter("formatter");
IValue ecn = kws.getParameter("explicitConstructorNames");
IValue edt = kws.getParameter("explicitDataTypes");
@@ -171,6 +172,7 @@ private static Response translateJsonResponse(Method method, IConstructor cons)
.setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'")
.setFormatters((IFunction) formatters)
.setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true)
+ .setRationalsAsString(ras != null ? ((IBool) ras).getValue() : false)
.setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false)
.setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false)
;
diff --git a/test/org/rascalmpl/test/rpc/IValueOverJsonTests.java b/test/org/rascalmpl/test/rpc/IValueOverJsonTests.java
index dbbdfaff5d9..115522aa92a 100644
--- a/test/org/rascalmpl/test/rpc/IValueOverJsonTests.java
+++ b/test/org/rascalmpl/test/rpc/IValueOverJsonTests.java
@@ -8,23 +8,35 @@
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.After;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.rascalmpl.ideservices.GsonUtils;
+import org.rascalmpl.ideservices.GsonUtils.ComplexTypeMode;
import org.rascalmpl.library.Prelude;
import org.rascalmpl.library.util.Math;
-import org.rascalmpl.values.RascalValueFactory;
import org.rascalmpl.values.ValueFactoryFactory;
+import com.google.gson.GsonBuilder;
+
import io.usethesource.vallang.IBool;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IDateTime;
@@ -37,34 +49,58 @@
import io.usethesource.vallang.IRational;
import io.usethesource.vallang.IReal;
import io.usethesource.vallang.ISet;
-import io.usethesource.vallang.ISetWriter;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.IValueFactory;
+import io.usethesource.vallang.type.Type;
+import io.usethesource.vallang.type.TypeFactory;
+import io.usethesource.vallang.type.TypeStore;
+@RunWith(Parameterized.class)
public class IValueOverJsonTests {
private static final IValueFactory vf = ValueFactoryFactory.getValueFactory();
private static final Prelude prelude = new Prelude(vf, null, null, null, null);
private static final Math math = new Math(vf);
- private static TestInterface server;
- private static PipedInputStream is0 = null, is1 = null;
- private static PipedOutputStream os0 = null, os1 = null;
-
- @BeforeClass
- public static void setup() throws IOException {
- is0 = new PipedInputStream();
- os0 = new PipedOutputStream();
- is1 = new PipedInputStream(os0);
- os1 = new PipedOutputStream(is0);
- new TestThread(is0, os0).start();
- new TestClient(is1, os1);
+ private static TypeFactory tf = TypeFactory.getInstance();
+ private static TypeStore ts = new TypeStore();
+ private static final Type TestAdt = tf.abstractDataType(ts, "TestAdt");
+ private static final Type TestAdt_testCons = tf.constructor(ts, TestAdt, "testCons", tf.stringType(), "id", tf.integerType(), "nr");
+
+ private JsonRpcTestInterface testServer;
+ private final PipedInputStream is0, is1;
+ private final PipedOutputStream os0, os1;
+
+ @Parameters(name="{0}")
+ public static Iterable