feat(muscript-gson): rename to muscript-json and port to commons-serialize-json
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2024-04-12 21:19:36 +02:00
parent 25259ba3b7
commit 24aab51a4b
Signed by: Johannes
GPG Key ID: E76429612C2929F4
39 changed files with 537 additions and 430 deletions

View File

@ -1,10 +1,3 @@
# μScript-gson
μScript-gson bridges the gap between μScript and commons-gson by supporting the serialization and deserialization of `Dynamic` to json and exposing this functionality to μScript scripts.
The gson type adapters are automatically registered to any `GsonHolder`, but the μScript methods need to be added to your `Scope` manually via `GsonLib.addTo(scope)`.
Other than that, no setup is needed.
# μScript
## Exposed functions
| signature | description |
|------------------------------|-------------------------------------------------------|
| `toJson(src: any): string` | Serializes `scr` to json |
| `fromJson(src: string): any` | Deserialized `src` into the appropriate μScript types |
This document was moved [here](../muscript-json/README.md).

View File

@ -1,30 +0,0 @@
package io.gitlab.jfronny.muscript.gson;
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.additional.context.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DList;
import io.gitlab.jfronny.muscript.data.dynamic.DString;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
public class GsonLib {
public static Scope addTo(Scope scope) {
return scope
.set("toJson", GsonLib::toJson)
.set("fromJson", GsonLib::fromJson);
}
public static DString toJson(DList args) {
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for toJson: expected 1 or 2 but got " + args.size());
Dynamic source = args.get(0);
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
return DFinal.of((lenient ? GsonHolders.CONFIG : GsonHolders.API).getGson().toJson(source));
}
public static Dynamic fromJson(DList args) {
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for fromJson: expected 1 or 2 but got " + args.size());
String source = args.get(0).asString().getValue();
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
return (lenient ? GsonHolders.CONFIG : GsonHolders.API).getGson().fromJson(source, Dynamic.class);
}
}

View File

@ -1,11 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl;
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
import io.gitlab.jfronny.gson.GsonBuilder;
public class DynamicGsonTransformer implements GsonTransformer {
@Override
public void apply(GsonBuilder builder) {
new DynamicSerializer().registerTo(builder);
}
}

View File

@ -1,72 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl;
import io.gitlab.jfronny.gson.GsonBuilder;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DDate;
import io.gitlab.jfronny.muscript.data.additional.DEnum;
import io.gitlab.jfronny.muscript.data.additional.DTime;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.gson.impl.typed.*;
import io.gitlab.jfronny.muscript.gson.impl.typed.additional.*;
import java.io.IOException;
public class DynamicSerializer extends TypeAdapter<Dynamic> {
public final DObjectSerializer dObject = new DObjectSerializer(this);
public final DListSerializer dList = new DListSerializer(this);
public final DNullSerializer dNull = new DNullSerializer();
public final DBoolSerializer dBool = new DBoolSerializer();
public final DNumberSerializer dNumber = new DNumberSerializer();
public final DStringSerializer dString = new DStringSerializer(this);
public final DCallableSerializer dCallable = new DCallableSerializer();
public final DynamicBaseSerializer dynamicBase = new DynamicBaseSerializer();
public final DDateSerializer dDate = new DDateSerializer();
public final DTimeSerializer dTime = new DTimeSerializer();
public final DEnumSerializer dEnum = new DEnumSerializer();
public void registerTo(GsonBuilder builder) {
builder.registerTypeAdapter(Dynamic.class, this);
builder.registerTypeHierarchyAdapter(DObject.class, dObject);
builder.registerTypeHierarchyAdapter(DList.class, dList);
builder.registerTypeAdapter(DNull.class, dNull);
builder.registerTypeHierarchyAdapter(DBool.class, dBool);
builder.registerTypeHierarchyAdapter(DNumber.class, dNumber);
builder.registerTypeHierarchyAdapter(DString.class, dString);
builder.registerTypeHierarchyAdapter(DCallable.class, dCallable);
builder.registerTypeHierarchyAdapter(DynamicBase.class, dynamicBase); // Custom dynamic implementations need custom support
builder.registerTypeAdapter(DDate.class, dDate);
builder.registerTypeAdapter(DTime.class, dTime);
builder.registerTypeAdapter(DEnum.class, dEnum);
}
@Override
public Dynamic read(JsonReader r) throws IOException {
return switch (r.peek()) {
case BEGIN_OBJECT -> dObject.read(r);
case BEGIN_ARRAY -> dList.read(r);
case NULL -> dNull.read(r);
case BOOLEAN -> dBool.read(r);
case NUMBER -> dNumber.read(r);
case STRING -> dString.read(r);
default -> throw new IllegalStateException("Unsupported token for beginning of Dynamic: " + r.peek());
};
}
@Override
public void write(JsonWriter w, Dynamic dynamic) throws IOException {
if (dynamic == null) w.nullValue();
else if (dynamic instanceof DDate date) dDate.write(w, date);
else if (dynamic instanceof DTime time) dTime.write(w, time);
else if (dynamic instanceof DEnum enm) dEnum.write(w, enm);
else if (dynamic.isObject()) dObject.write(w, dynamic.asObject());
else if (dynamic.isList()) dList.write(w, dynamic.asList());
else if (dynamic instanceof DNull n) dNull.write(w, n);
else if (dynamic.isBool()) dBool.write(w, dynamic.asBool());
else if (dynamic.isNumber()) dNumber.write(w, dynamic.asNumber());
else if (dynamic.isString()) dString.write(w, dynamic.asString());
else if (dynamic.isCallable()) dCallable.write(w, dynamic.asCallable());
else throw new IllegalArgumentException("Unexpected dynamic type for: " + dynamic);
}
}

View File

@ -1,21 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DBool;
import java.io.IOException;
public class DBoolSerializer extends TypeAdapter<DBool> {
@Override
public void write(JsonWriter w, DBool dynamic) throws IOException {
w.value(dynamic.getValue());
}
@Override
public DBool read(JsonReader r) throws IOException {
return DFinal.of(r.nextBoolean());
}
}

View File

@ -1,21 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.dynamic.DCallable;
import java.io.IOException;
public class DCallableSerializer extends TypeAdapter<DCallable> {
@Override
public void write(JsonWriter w, DCallable dynamic) throws IOException {
if (dynamic.getName().equals(DCallable.DEFAULT_NAME)) throw new IllegalArgumentException("Unnamed callables cannot be serialized to json");
else w.value(dynamic.getName());
}
@Override
public DCallable read(JsonReader r) throws IOException {
throw new UnsupportedOperationException("DCallables cannot be deserialized from json");
}
}

View File

@ -1,40 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DList;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public class DListSerializer extends TypeAdapter<DList> {
private final DynamicSerializer base;
public DListSerializer(DynamicSerializer base) {
this.base = base;
}
@Override
public void write(JsonWriter w, DList dynamic) throws IOException {
w.beginArray();
for (Dynamic value : dynamic.getValue()) {
base.write(w, value);
}
w.endArray();
}
@Override
public DList read(JsonReader r) throws IOException {
List<Dynamic> elements = new LinkedList<>();
r.beginArray();
while (r.peek() != JsonToken.END_ARRAY) {
elements.add(base.read(r));
}
r.endArray();
return DFinal.of(elements);
}
}

View File

@ -1,21 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
import java.io.IOException;
public class DNullSerializer extends TypeAdapter<DNull> {
@Override
public void write(JsonWriter w, DNull dynamic) throws IOException {
w.nullValue();
}
@Override
public DNull read(JsonReader r) throws IOException {
r.nextNull();
return new DNull();
}
}

View File

@ -1,23 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DNumber;
import java.io.IOException;
public class DNumberSerializer extends TypeAdapter<DNumber> {
@Override
public void write(JsonWriter w, DNumber dynamic) throws IOException {
double d = dynamic.getValue();
if (d % 1.0 != 0 || d > Long.MAX_VALUE) w.value(d);
else w.value((long) d);
}
@Override
public DNumber read(JsonReader r) throws IOException {
return DFinal.of(r.nextDouble());
}
}

View File

@ -1,41 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
public class DObjectSerializer extends TypeAdapter<DObject> {
private final DynamicSerializer base;
public DObjectSerializer(DynamicSerializer base) {
this.base = base;
}
@Override
public void write(JsonWriter w, DObject dynamic) throws IOException {
w.beginObject();
for (Map.Entry<String, ? extends Dynamic> entry : dynamic.getValue().entrySet()) {
w.name(entry.getKey());
base.write(w, entry.getValue());
}
w.endObject();
}
@Override
public DObject read(JsonReader r) throws IOException {
Map<String, Dynamic> elements = new LinkedHashMap<>();
r.beginObject();
while (r.peek() != JsonToken.END_OBJECT) {
elements.put(r.nextName(), base.read(r));
}
r.endObject();
return DFinal.of(elements);
}
}

View File

@ -1,28 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DString;
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
import java.io.IOException;
public class DStringSerializer extends TypeAdapter<DString> {
private final DynamicSerializer base;
public DStringSerializer(DynamicSerializer base) {
this.base = base;
}
@Override
public void write(JsonWriter w, DString dynamic) throws IOException {
w.value(dynamic.getValue());
}
@Override
public DString read(JsonReader r) throws IOException {
if (r.peek() == JsonToken.STRING) return DFinal.of(r.nextString());
else return base.read(r).asString();
}
}

View File

@ -1,20 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicBase;
import java.io.IOException;
public class DynamicBaseSerializer extends TypeAdapter<DynamicBase> {
@Override
public void write(JsonWriter w, DynamicBase dynamic) throws IOException {
throw new UnsupportedOperationException("Tried to write unsupported custom dynamic: " + dynamic);
}
@Override
public DynamicBase read(JsonReader r) throws IOException {
throw new UnsupportedOperationException("Tried to read unsupported custom dynamic");
}
}

View File

@ -1,22 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DDate;
import java.io.IOException;
import java.time.LocalDate;
public class DDateSerializer extends TypeAdapter<DDate> {
@Override
public void write(JsonWriter w, DDate dynamic) throws IOException {
w.value(dynamic.toString());
}
@Override
public DDate read(JsonReader r) throws IOException {
String s = r.nextString();
return new DDate(() -> LocalDate.parse(s));
}
}

View File

@ -1,26 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DEnum;
import java.io.IOException;
public class DEnumSerializer extends TypeAdapter<DEnum> {
@Override
public void write(JsonWriter w, DEnum dynamic) throws IOException {
if (dynamic.value() == null) {
w.beginArray();
for (String s : dynamic.values().keySet()) {
w.value(s);
}
w.endArray();
} else w.value(dynamic.value().value());
}
@Override
public DEnum read(JsonReader r) throws IOException {
throw new UnsupportedOperationException("Deserializing DEnum is unsupported");
}
}

View File

@ -1,22 +0,0 @@
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
import io.gitlab.jfronny.gson.TypeAdapter;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.DTime;
import java.io.IOException;
import java.time.LocalTime;
public class DTimeSerializer extends TypeAdapter<DTime> {
@Override
public void write(JsonWriter w, DTime dynamic) throws IOException {
w.value(dynamic.toString());
}
@Override
public DTime read(JsonReader r) throws IOException {
String s = r.nextString();
return new DTime(() -> LocalTime.parse(s));
}
}

View File

@ -1,7 +0,0 @@
module io.gitlab.jfronny.commons.muscript.gson {
requires io.gitlab.jfronny.gson;
requires io.gitlab.jfronny.commons.muscript.data.additional;
requires io.gitlab.jfronny.commons.serialize.gson;
requires io.gitlab.jfronny.commons.muscript.data;
exports io.gitlab.jfronny.muscript.gson;
}

View File

@ -1 +0,0 @@
io.gitlab.jfronny.muscript.gson.impl.DynamicGsonTransformer

10
muscript-json/README.md Normal file
View File

@ -0,0 +1,10 @@
# μScript-gson
μScript-gson bridges the gap between μScript and commons-serialize-json by supporting the serialization and deserialization of `Dynamic` to json and exposing this functionality to μScript scripts.
The gson type adapters are automatically registered via the ServiceLoader mechanism, but the μScript methods need to be added to your `Scope` manually via `JsonLib.addTo(scope)`.
Other than that, no setup is needed.
## Exposed functions
| signature | description |
|------------------------------|-------------------------------------------------------|
| `toJson(src: any): string` | Serializes `scr` to json |
| `fromJson(src: string): any` | Deserialized `src` into the appropriate μScript types |

View File

@ -5,8 +5,10 @@ plugins {
}
dependencies {
implementation(projects.commons)
implementation(projects.muscriptDataAdditional)
implementation(projects.commonsGson)
implementation(projects.commonsSerializeJson)
implementation(projects.commonsSerializeDatabind)
testImplementation(libs.junit.jupiter.api)
testImplementation(projects.muscriptParser)
@ -18,7 +20,7 @@ publishing {
publications {
create<MavenPublication>("maven") {
groupId = "io.gitlab.jfronny"
artifactId = "muscript-gson"
artifactId = "muscript-json"
from(components["java"])
}
@ -28,7 +30,8 @@ publishing {
tasks.javadoc {
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons/$version/raw", projects.commons)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons-serialize/$version/raw", projects.commonsSerialize)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons-gson/$version/raw", projects.commonsGson)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons-serialize-json/$version/raw", projects.commonsSerializeJson)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons-serialize-databind/$version/raw", projects.commonsSerializeJson)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/muscript-data-dynamic/$version/raw", projects.muscriptDataDynamic)
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/muscript-data-additional/$version/raw", projects.muscriptDataAdditional)
}

View File

@ -0,0 +1,51 @@
package io.gitlab.jfronny.muscript.json;
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
import io.gitlab.jfronny.commons.serialize.json.JsonTransport;
import io.gitlab.jfronny.commons.serialize.json.JsonWriter;
import io.gitlab.jfronny.muscript.data.additional.context.Scope;
import java.io.IOException;
import java.io.Reader;
public class JsonLib {
public static Scope addTo(Scope scope) {
return new TransportLib<>(new LenientTransport(), new StrictTransport(), "toJson", "fromJson").addTo(scope);
}
private static class StrictTransport extends JsonTransport {
@Override
public JsonReader createReader(Reader source) {
return super.createReader(source)
.setLenient(false)
.setSerializeSpecialFloatingPointValues(true);
}
@Override
public String write(PerformWrite<IOException, JsonWriter> write) throws IOException {
return super.write(writer -> write.write(writer.setLenient(false)
.setNewline("")
.setIndent("")
.setSerializeSpecialFloatingPointValues(true)
.setSerializeNulls(true)));
}
}
private static class LenientTransport extends JsonTransport {
@Override
public JsonReader createReader(Reader source) {
return super.createReader(source)
.setLenient(true)
.setSerializeSpecialFloatingPointValues(true);
}
@Override
public String write(PerformWrite<IOException, JsonWriter> write) throws IOException {
return super.write(writer -> write.write(writer.setLenient(true)
.setNewline("\n")
.setIndent(" ")
.setSerializeSpecialFloatingPointValues(true)
.setSerializeNulls(true)));
}
}
}

View File

@ -0,0 +1,66 @@
package io.gitlab.jfronny.muscript.json;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Transport;
import io.gitlab.jfronny.commons.serialize.databind.DatabindSerializer;
import io.gitlab.jfronny.commons.serialize.databind.ObjectMapper;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.additional.context.Scope;
import io.gitlab.jfronny.muscript.data.additional.libs.IntentionalException;
import io.gitlab.jfronny.muscript.data.dynamic.DList;
import io.gitlab.jfronny.muscript.data.dynamic.DString;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
import java.io.IOException;
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.BOOL;
public class TransportLib<TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>, Writer extends SerializeWriter<TEx, Writer>> {
private final DatabindSerializer<TEx, Reader, Writer> lenientBackend;
private final DatabindSerializer<TEx, Reader, Writer> strictBackend;
private final String serializeName;
private final String deserializeName;
public TransportLib(Transport<TEx, Reader, Writer> lenientTransport, Transport<TEx, Reader, Writer> strictTransport, String serializeName, String deserializeName) {
ObjectMapper mapper = new ObjectMapper();
this.lenientBackend = new DatabindSerializer<>(lenientTransport, mapper);
this.strictBackend = new DatabindSerializer<>(strictTransport, mapper);
this.serializeName = serializeName;
this.deserializeName = deserializeName;
}
public Scope addTo(Scope scope) {
return scope
.set(serializeName, serialize, this::serialize)
.set(deserializeName, deserialize, this::deserialize);
}
private final DType serialize = callable(STRING, arg("value", generic(0)))
.and(callable(STRING, arg("value", generic(0)), arg("lenient", BOOL)));
public DString serialize(DList args) {
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for toJson: expected 1 or 2 but got " + args.size());
Dynamic source = args.get(0);
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
try {
return DFinal.of((lenient ? lenientBackend : strictBackend).serialize(source));
} catch (IOException e) {
throw new IntentionalException("Could not serialize value: " + e.getMessage());
}
}
private final DType deserialize = callable(generic(0), arg("source", STRING))
.and(callable(generic(0), arg("source", STRING), arg("lenient", BOOL)));
public Dynamic deserialize(DList args) {
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for fromJson: expected 1 or 2 but got " + args.size());
String source = args.get(0).asString().getValue();
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
try {
return (lenient ? lenientBackend : strictBackend).deserialize(source, Dynamic.class);
} catch (IOException e) {
throw new IntentionalException("Could not deserialize value: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,61 @@
package io.gitlab.jfronny.muscript.json.impl;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DDate;
import io.gitlab.jfronny.muscript.data.additional.DEnum;
import io.gitlab.jfronny.muscript.data.additional.DTime;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.json.impl.typed.*;
import io.gitlab.jfronny.muscript.json.impl.typed.additional.*;
@SerializerFor(targets = Dynamic.class)
public class DynamicSerializer extends TypeAdapter<Dynamic> {
public final DObjectSerializer dObject = new DObjectSerializer();
public final DListSerializer dList = new DListSerializer();
public final DNullSerializer dNull = new DNullSerializer();
public final DBoolSerializer dBool = new DBoolSerializer();
public final DNumberSerializer dNumber = new DNumberSerializer();
public final DStringSerializer dString = new DStringSerializer();
public final DCallableSerializer dCallable = new DCallableSerializer();
public final DynamicBaseSerializer dynamicBase = new DynamicBaseSerializer();
public final DDateSerializer dDate = new DDateSerializer();
public final DTimeSerializer dTime = new DTimeSerializer();
public final DEnumSerializer dEnum = new DEnumSerializer();
public DynamicSerializer() {
DynamicSerializerHolder.setInstance(this);
}
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(Dynamic value, Writer writer) throws TEx, MalformedDataException {
if (value == null) writer.nullValue();
else if (value instanceof DDate date) dDate.serialize(date, writer);
else if (value instanceof DTime time) dTime.serialize(time, writer);
else if (value instanceof DEnum enm) dEnum.serialize(enm, writer);
else if (value.isObject()) dObject.serialize(value.asObject(), writer);
else if (value.isList()) dList.serialize(value.asList(), writer);
else if (value instanceof DNull n) dNull.serialize(n, writer);
else if (value.isBool()) dBool.serialize(value.asBool(), writer);
else if (value.isNumber()) dNumber.serialize(value.asNumber(), writer);
else if (value.isString()) dString.serialize(value.asString(), writer);
else if (value.isCallable()) dCallable.serialize(value.asCallable(), writer);
else throw new IllegalArgumentException("Unexpected dynamic type for: " + value);
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> Dynamic deserialize(Reader reader) throws TEx, MalformedDataException {
return switch (reader.peek()) {
case BEGIN_OBJECT -> dObject.deserialize(reader);
case BEGIN_ARRAY -> dList.deserialize(reader);
case NULL -> dNull.deserialize(reader);
case BOOLEAN -> dBool.deserialize(reader);
case NUMBER -> dNumber.deserialize(reader);
case STRING -> dString.deserialize(reader);
default -> throw new IllegalStateException("Unsupported token for beginning of Dynamic: " + reader.peek());
};
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.muscript.json.impl;
import java.util.Objects;
public class DynamicSerializerHolder {
private static DynamicSerializer INSTANCE;
public static void setInstance(DynamicSerializer instance) {
INSTANCE = instance;
}
public static DynamicSerializer getInstance() {
return Objects.requireNonNull(INSTANCE);
}
}

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DBool;
@SerializerFor(targets = DBool.class, hierarchical = true)
public class DBoolSerializer extends TypeAdapter<DBool> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DBool value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.getValue());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DBool deserialize(Reader reader) throws TEx, MalformedDataException {
return DFinal.of(reader.nextBoolean());
}
}

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.dynamic.DCallable;
@SerializerFor(targets = DCallable.class, hierarchical = true)
public class DCallableSerializer extends TypeAdapter<DCallable> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DCallable value, Writer writer) throws TEx, MalformedDataException {
if (value.getName().equals(DCallable.DEFAULT_NAME)) throw new IllegalArgumentException("Unnamed callables cannot be serialized to json");
else writer.value(value.getName());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DCallable deserialize(Reader reader) throws TEx, MalformedDataException {
throw new UnsupportedOperationException("DCallables cannot be deserialized from json");
}
}

View File

@ -0,0 +1,38 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Token;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DList;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.json.impl.DynamicSerializerHolder;
import java.util.LinkedList;
import java.util.List;
@SerializerFor(targets = DList.class, hierarchical = true)
public class DListSerializer extends TypeAdapter<DList> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DList value, Writer writer) throws TEx, MalformedDataException {
writer.beginArray();
for (Dynamic v : value.getValue()) {
DynamicSerializerHolder.getInstance().serialize(v, writer);
}
writer.endArray();
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DList deserialize(Reader reader) throws TEx, MalformedDataException {
List<Dynamic> elements = new LinkedList<>();
reader.beginArray();
while (reader.peek() != Token.END_ARRAY) {
elements.add(DynamicSerializerHolder.getInstance().deserialize(reader));
}
reader.endArray();
return DFinal.of(elements);
}
}

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
@SerializerFor(targets = DNull.class, hierarchical = true)
public class DNullSerializer extends TypeAdapter<DNull> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DNull value, Writer writer) throws TEx, MalformedDataException {
writer.nullValue();
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DNull deserialize(Reader reader) throws TEx, MalformedDataException {
reader.nextNull();
return new DNull();
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DNumber;
@SerializerFor(targets = DNumber.class, hierarchical = true)
public class DNumberSerializer extends TypeAdapter<DNumber> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DNumber value, Writer writer) throws TEx, MalformedDataException {
double d = value.getValue();
if (d % 1.0 != 0 || d > Long.MAX_VALUE) writer.value(d);
else writer.value((long) d);
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DNumber deserialize(Reader reader) throws TEx, MalformedDataException {
return DFinal.of(reader.nextDouble());
}
}

View File

@ -0,0 +1,39 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Token;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.json.impl.DynamicSerializerHolder;
import java.util.LinkedHashMap;
import java.util.Map;
@SerializerFor(targets = DObject.class, hierarchical = true)
public class DObjectSerializer extends TypeAdapter<DObject> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DObject value, Writer writer) throws TEx, MalformedDataException {
writer.beginObject();
for (Map.Entry<String, ? extends Dynamic> entry : value.getValue().entrySet()) {
writer.name(entry.getKey());
DynamicSerializerHolder.getInstance().serialize(entry.getValue(), writer);
}
writer.endObject();
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DObject deserialize(Reader reader) throws TEx, MalformedDataException {
Map<String, Dynamic> elements = new LinkedHashMap<>();
reader.beginObject();
while (reader.peek() != Token.END_OBJECT) {
elements.put(reader.nextName(), DynamicSerializerHolder.getInstance().deserialize(reader));
}
reader.endObject();
return DFinal.of(elements);
}
}

View File

@ -0,0 +1,25 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.Token;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.dynamic.DString;
import io.gitlab.jfronny.muscript.json.impl.DynamicSerializerHolder;
@SerializerFor(targets = DString.class, hierarchical = true)
public class DStringSerializer extends TypeAdapter<DString> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DString value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.getValue());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DString deserialize(Reader reader) throws TEx, MalformedDataException {
if (reader.peek() == Token.STRING) return DFinal.of(reader.nextString());
else return DynamicSerializerHolder.getInstance().deserialize(reader).asString();
}
}

View File

@ -0,0 +1,21 @@
package io.gitlab.jfronny.muscript.json.impl.typed;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicBase;
@SerializerFor(targets = DynamicBase.class, hierarchical = true)
public class DynamicBaseSerializer extends TypeAdapter<DynamicBase> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DynamicBase value, Writer writer) throws TEx, MalformedDataException {
throw new UnsupportedOperationException("Tried to write unsupported custom dynamic: " + value);
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DynamicBase deserialize(Reader reader) throws TEx, MalformedDataException {
throw new UnsupportedOperationException("Tried to read unsupported custom dynamic");
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.muscript.json.impl.typed.additional;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DDate;
import java.time.LocalDate;
@SerializerFor(targets = DDate.class, hierarchical = true)
public class DDateSerializer extends TypeAdapter<DDate> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DDate value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DDate deserialize(Reader reader) throws TEx, MalformedDataException {
String s = reader.nextString();
return new DDate(() -> LocalDate.parse(s));
}
}

View File

@ -0,0 +1,27 @@
package io.gitlab.jfronny.muscript.json.impl.typed.additional;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DEnum;
@SerializerFor(targets = DEnum.class, hierarchical = true)
public class DEnumSerializer extends TypeAdapter<DEnum> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DEnum value, Writer writer) throws TEx, MalformedDataException {
if (value.value() == null) {
writer.beginArray();
for (String s : value.values().keySet()) {
writer.value(s);
}
writer.endArray();
} else writer.value(value.value().value());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DEnum deserialize(Reader reader) throws TEx, MalformedDataException {
throw new UnsupportedOperationException("Deserializing DEnum is unsupported");
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.muscript.json.impl.typed.additional;
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
import io.gitlab.jfronny.commons.serialize.SerializeReader;
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
import io.gitlab.jfronny.commons.serialize.databind.SerializerFor;
import io.gitlab.jfronny.commons.serialize.databind.TypeAdapter;
import io.gitlab.jfronny.muscript.data.additional.DTime;
import java.time.LocalTime;
@SerializerFor(targets = DTime.class, hierarchical = true)
public class DTimeSerializer extends TypeAdapter<DTime> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DTime value, Writer writer) throws TEx, MalformedDataException {
writer.value(value.toString());
}
@Override
public <TEx extends Throwable, Reader extends SerializeReader<TEx, Reader>> DTime deserialize(Reader reader) throws TEx, MalformedDataException {
String s = reader.nextString();
return new DTime(() -> LocalTime.parse(s));
}
}

View File

@ -0,0 +1,9 @@
module io.gitlab.jfronny.commons.muscript.json {
requires io.gitlab.jfronny.commons;
requires io.gitlab.jfronny.commons.muscript.data.additional;
requires io.gitlab.jfronny.commons.muscript.data;
requires io.gitlab.jfronny.commons.serialize.json;
requires io.gitlab.jfronny.commons.serialize.databind;
requires io.gitlab.jfronny.commons.serialize;
exports io.gitlab.jfronny.muscript.json;
}

View File

@ -0,0 +1,12 @@
io.gitlab.jfronny.muscript.json.impl.typed.additional.DDateSerializer
io.gitlab.jfronny.muscript.json.impl.typed.additional.DEnumSerializer
io.gitlab.jfronny.muscript.json.impl.typed.additional.DTimeSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DBoolSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DCallableSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DListSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DNullSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DNumberSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DObjectSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DStringSerializer
io.gitlab.jfronny.muscript.json.impl.typed.DynamicBaseSerializer
io.gitlab.jfronny.muscript.json.impl.DynamicSerializer

View File

@ -1,15 +1,18 @@
package io.gitlab.jfronny.muscript.gson.test;
package io.gitlab.jfronny.muscript.json.test;
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
import io.gitlab.jfronny.commons.serialize.databind.DatabindSerializer;
import io.gitlab.jfronny.commons.serialize.databind.ObjectMapper;
import io.gitlab.jfronny.commons.serialize.json.JsonTransport;
import io.gitlab.jfronny.muscript.core.MuScriptVersion;
import io.gitlab.jfronny.muscript.data.additional.DFinal;
import io.gitlab.jfronny.muscript.data.additional.context.Scope;
import io.gitlab.jfronny.muscript.data.additional.libs.StandardLib;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.gson.GsonLib;
import io.gitlab.jfronny.muscript.json.JsonLib;
import io.gitlab.jfronny.muscript.parser.Parser;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
@ -20,14 +23,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class JsonTest {
@Test
void simpleSerialize() {
void simpleSerialize() throws IOException {
assertEquals("\"Yes\"", serialize(of("Yes")));
assertEquals("{\"key\":3}", serialize(of(Map.of("key", of(3)))));
assertEquals("[true,12,7]", serialize(of(of(true), of(12), of(7).asString()))); // string lens ignored
}
@Test
void simpleDeserialize() {
void simpleDeserialize() throws IOException {
assertEquals(of("Yes"), deserialize("\"Yes\"", Dynamic.class));
assertEquals(of(Map.of("key", of(3))), deserialize("{\"key\":3}", Dynamic.class));
assertEquals(of(of(true), of(12), of("7")), deserialize("[true,12,\"7\"]", Dynamic.class));
@ -40,7 +43,7 @@ class JsonTest {
}
private Scope createScope() {
return GsonLib.addTo(StandardLib.createScope(MuScriptVersion.DEFAULT));
return JsonLib.addTo(StandardLib.createScope(MuScriptVersion.DEFAULT));
}
private Dynamic execute(String source, String... args) {
@ -50,11 +53,13 @@ class JsonTest {
);
}
private String serialize(Object object) {
return GsonHolders.API.getGson().toJson(object);
private DatabindSerializer serializer = new DatabindSerializer(new JsonTransport(), new ObjectMapper());
private String serialize(Object object) throws IOException {
return serializer.serialize(object);
}
private <T> T deserialize(String source, Class<T> klazz) {
return GsonHolders.API.getGson().fromJson(source, klazz);
private <T> T deserialize(String source, Class<T> klazz) throws IOException {
return serializer.deserialize(source, klazz);
}
}

View File

@ -73,5 +73,5 @@ The syntax used for signatures in this list is not used elsewhere and not part o
| `values(ob: object): list` | Returns the list of values of entries of the object `ob` |
| `try(closure: {any... -> any}, args: any...): try` | Attempts to execute the closure with the provided args and returns a `try`. For details on what to do with that, look above. |
Other project-specific functions (like [μScript-gson](../muscript-gson/README.md)) will likely also be available to your scripts.
Other project-specific functions (like [μScript-gson](../muscript-json/README.md)) will likely also be available to your scripts.
Please consult the documentation for the project using μScript for more information on what is available or use `this::keys()` to view everything.

View File

@ -22,6 +22,6 @@ include("muscript-parser")
include("muscript-serialize")
include("muscript-optimizer")
include("muscript-runtime")
include("muscript-gson")
include("muscript-json")
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")