diff --git a/src/org/rascalmpl/library/Content.rsc b/src/org/rascalmpl/library/Content.rsc index 28be88bbe94..0075670e713 100644 --- a/src/org/rascalmpl/library/Content.rsc +++ b/src/org/rascalmpl/library/Content.rsc @@ -80,7 +80,7 @@ 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\'") + | jsonResponse(Status status, map[str,str] header, value val, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'", bool explicitConstructorNames=false, bool explicitDataTypes=false) ; diff --git a/src/org/rascalmpl/library/lang/json/IO.java b/src/org/rascalmpl/library/lang/json/IO.java index 6cecd64debc..c21db87dbe8 100644 --- a/src/org/rascalmpl/library/lang/json/IO.java +++ b/src/org/rascalmpl/library/lang/json/IO.java @@ -137,7 +137,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) { + public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt, IInteger indent, IBool dropOrigins, IBool explicitConstructorNames, IBool explicitDataTypes) { try (JsonWriter out = new JsonWriter(new OutputStreamWriter(URIResolverRegistry.getInstance().getOutputStream(loc, false), Charset.forName("UTF8")))) { if (indent.intValue() > 0) { out.setIndent(" ".substring(0, indent.intValue() % 9)); @@ -148,13 +148,15 @@ public void writeJSON(ISourceLocation loc, IValue value, IBool unpackedLocations .setDatesAsInt(dateTimeAsInt.getValue()) .setUnpackedLocations(unpackedLocations.getValue()) .setDropOrigins(dropOrigins.getValue()) + .setExplicitConstructorNames(explicitConstructorNames.getValue()) + .setExplicitDataTypes(explicitDataTypes.getValue()) .write(out, value); } catch (IOException e) { throw RuntimeExceptionFactory.io(values.string(e.getMessage()), null, null); } } - public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt, IInteger indent, IBool dropOrigins) { + public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFormat, IBool dateTimeAsInt, IInteger indent, IBool dropOrigins, IBool explicitConstructorNames, IBool explicitDataTypes) { StringWriter string = new StringWriter(); try (JsonWriter out = new JsonWriter(string)) { @@ -166,6 +168,8 @@ public IString asJSON(IValue value, IBool unpackedLocations, IString dateTimeFor .setDatesAsInt(dateTimeAsInt.getValue()) .setUnpackedLocations(unpackedLocations.getValue()) .setDropOrigins(dropOrigins.getValue()) + .setExplicitConstructorNames(explicitConstructorNames.getValue()) + .setExplicitDataTypes(explicitDataTypes.getValue()) .write(out, value); return values.string(string.toString()); diff --git a/src/org/rascalmpl/library/lang/json/IO.rsc b/src/org/rascalmpl/library/lang/json/IO.rsc index 3c8a9f3dede..661a062fa23 100644 --- a/src/org/rascalmpl/library/lang/json/IO.rsc +++ b/src/org/rascalmpl/library/lang/json/IO.rsc @@ -15,25 +15,20 @@ module lang::json::IO @javaClass{org.rascalmpl.library.lang.json.IO} -@deprecated{ -use writeJSON -} +@deprecated{use writeJSON} public java str toJSON(value v); @javaClass{org.rascalmpl.library.lang.json.IO} -@deprecated{ -use asJSON -} +@deprecated{use asJSON} public java str toJSON(value v, bool compact); @javaClass{org.rascalmpl.library.lang.json.IO} -@deprecated{ -use readJSON -} +@deprecated{use readJSON} public java &T fromJSON(type[&T] typ, str src); @javaClass{org.rascalmpl.library.lang.json.IO} -@synopsis{reads JSON values from a stream +@synopsis{reads JSON values from a stream} +@description{ In general the translation behaves as follows: * Objects translate to map[str,value] by default, unless a node is expected (properties are then translated to keyword fields) * Arrays translate to lists by default, or to a set if that is expected or a tuple if that is expected. Arrays may also be interpreted as constructors or nodes (see below) @@ -52,7 +47,24 @@ In general the translation behaves as the same as for ((readJSON)).} java &T parseJSON(type[&T] expected, str src, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool lenient=false, bool trackOrigins=false); @javaClass{org.rascalmpl.library.lang.json.IO} -java void writeJSON(loc target, value val, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent=0, bool dropOrigins=true); +@synopsis{Serializes a value as a JSON string and stream it} +@description{ +This function tries to map Rascal values to JSON values in a natural way. +In particular it tries to create a value that has the same number of recursive levels, +such that one constructor maps to one object. The serialization is typically _lossy_ since +JSON values by default do not explicitly encode the class or constructor while Rascal data types do. + +If you need the names of constructors or data-types in your result, then use the parameters: +* `explicitConstructorNames=true` will store the name of every constructor in a field `_constructor` +* `explicitDataTypes=true` will store the name of the ADT in a field called `_type` + +The `dateTimeFormat` parameter dictates how `datetime` values will be printed. + +The `unpackedLocations` parameter will produce an object with many fields for every property of a `loc` value, but +if set to false a `loc` will be printed as a string. +} +java void writeJSON(loc target, value val, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent=0, bool dropOrigins=true, bool explicitConstructorNames=false, bool explicitDataTypes=false); @javaClass{org.rascalmpl.library.lang.json.IO} -java str asJSON(value val, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent = 0, bool dropOrigins=true); +@synopsis{Serializes a value as a JSON string and stores it as a string} +java str asJSON(value val, bool unpackedLocations=false, str dateTimeFormat="yyyy-MM-dd\'T\'HH:mm:ssZZZZZ", bool dateTimeAsInt=false, int indent = 0, bool dropOrigins=true, bool explicitConstructorNames=false, bool explicitDataTypes=false); diff --git a/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java b/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java index 8fc7452cd91..9ace3155adb 100644 --- a/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java +++ b/src/org/rascalmpl/library/lang/json/internal/JsonValueWriter.java @@ -44,6 +44,8 @@ public class JsonValueWriter { private boolean datesAsInts = true; private boolean unpackedLocations = false; private boolean dropOrigins = true; + private boolean explicitConstructorNames = false; + private boolean explicitDataTypes; public JsonValueWriter() { setCalendarFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); @@ -78,6 +80,16 @@ public JsonValueWriter setDropOrigins(boolean setting) { return this; } + public JsonValueWriter setExplicitConstructorNames(boolean setting) { + this.explicitConstructorNames = setting; + return this; + } + + public JsonValueWriter setExplicitDataTypes(boolean setting) { + this.explicitDataTypes = setting; + return this; + } + public void write(JsonWriter out, IValue value) throws IOException { value.accept(new IValueVisitor() { @@ -229,6 +241,17 @@ public Void visitConstructor(IConstructor o) throws IOException { } out.beginObject(); + + if (explicitConstructorNames) { + out.name("_constructor"); + out.value(o.getName()); + } + + if (explicitDataTypes) { + out.name("_type"); + out.value(o.getType().getName()); + } + int i = 0; for (IValue arg : o) { out.name(o.getConstructorType().getFieldName(i)); diff --git a/src/org/rascalmpl/library/util/TermREPL.java b/src/org/rascalmpl/library/util/TermREPL.java index f149bb36340..8ed61b9d41e 100644 --- a/src/org/rascalmpl/library/util/TermREPL.java +++ b/src/org/rascalmpl/library/util/TermREPL.java @@ -238,10 +238,15 @@ private void handleJSONResponse(Map output, IConstructor re IValue dtf = kws.getParameter("dateTimeFormat"); IValue dai = kws.getParameter("dateTimeAsInt"); - + IValue ecn = kws.getParameter("explicitConstructorNames"); + IValue edt = kws.getParameter("explicitDataTypes"); + JsonValueWriter writer = new JsonValueWriter() .setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'") - .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true); + .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true) + .setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false) + .setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false) + ; final ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/src/org/rascalmpl/library/util/Webserver.java b/src/org/rascalmpl/library/util/Webserver.java index 87a2d14ee4a..6cff5ec98f1 100644 --- a/src/org/rascalmpl/library/util/Webserver.java +++ b/src/org/rascalmpl/library/util/Webserver.java @@ -238,10 +238,15 @@ private Response translateJsonResponse(Method method, IConstructor cons) { IValue dtf = kws.getParameter("dateTimeFormat"); IValue dai = kws.getParameter("dateTimeAsInt"); + IValue ecn = kws.getParameter("explicitConstructorNames"); + IValue edt = kws.getParameter("explicitDataTypes"); JsonValueWriter writer = new JsonValueWriter() .setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'") - .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true); + .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true) + .setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false) + .setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false) + ; try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/src/org/rascalmpl/repl/REPLContentServer.java b/src/org/rascalmpl/repl/REPLContentServer.java index de811bcb6ef..becc0a80871 100644 --- a/src/org/rascalmpl/repl/REPLContentServer.java +++ b/src/org/rascalmpl/repl/REPLContentServer.java @@ -135,10 +135,15 @@ private static Response translateJsonResponse(Method method, IConstructor cons) IValue dtf = kws.getParameter("dateTimeFormat"); IValue dai = kws.getParameter("dateTimeAsInt"); + IValue ecn = kws.getParameter("explicitConstructorNames"); + IValue edt = kws.getParameter("explicitDataTypes"); JsonValueWriter writer = new JsonValueWriter() .setCalendarFormat(dtf != null ? ((IString) dtf).getValue() : "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'") - .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true); + .setDatesAsInt(dai != null ? ((IBool) dai).getValue() : true) + .setExplicitConstructorNames(ecn != null ? ((IBool) ecn).getValue() : false) + .setExplicitDataTypes(edt != null ? ((IBool) edt).getValue() : false) + ; try { final ByteArrayOutputStream baos = new ByteArrayOutputStream();