This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 0db7e238dc ISIS-3127: [RO] render composite types as Map
0db7e238dc is described below
commit 0db7e238dc4b0af9ffafa3c7646e813897136874
Author: andi-huber <[email protected]>
AuthorDate: Fri Aug 19 15:56:16 2022 +0200
ISIS-3127: [RO] render composite types as Map
---
.../isis/testdomain/rest/JsonValueEncoderTest.java | 42 ++++++++++++++--
.../restfulobjects/applib/JsonRepresentation.java | 27 ++++++++---
.../domainobjects/JsonValueConverters.java | 15 ++++--
.../rendering/domainobjects/JsonValueEncoder.java | 56 ++++++++++++++++------
.../domainobjects/ObjectPropertyReprRenderer.java | 8 ++--
5 files changed, 114 insertions(+), 34 deletions(-)
diff --git
a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
index 0970b002c2..da1a0ea318 100644
---
a/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
+++
b/regressiontests/stable-rest/src/test/java/org/apache/isis/testdomain/rest/JsonValueEncoderTest.java
@@ -30,9 +30,9 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.apache.isis.applib.value.Blob;
+import org.apache.isis.applib.value.Clob;
import org.apache.isis.applib.value.NamedWithMimeType.CommonMimeType;
import org.apache.isis.commons.internal.base._Temporals;
-import org.apache.isis.commons.internal.functions._Functions;
import org.apache.isis.core.config.presets.IsisPresets;
import org.apache.isis.core.metamodel.context.MetaModelContext;
import org.apache.isis.testdomain.conf.Configuration_headless;
@@ -368,10 +368,41 @@ class JsonValueEncoderTest {
@Test
void whenBlob() {
val value = Blob.of("a Blob", CommonMimeType.BIN, new byte[] {1, 2,
3});
- val representation = representationFor(value,
_Functions.noopConsumer());
+ val representation = representationFor(value, osObj->assertEquals(
+ JsonRepresentation.newMap("name", "a Blob.bin",
+ "mimeType", "application/octet-stream",
+ "bytes", "AQID").toString(),
+ osObj.toString()));
- System.err.printf("representation %s%n", representation);
- //TODO convert repr. to map like
+ assertThat(representation.getString("extensions.x-isis-format"),
is("blob"));
+ }
+
+ @Test
+ void whenClob() {
+ val value = Clob.of("a Clob", CommonMimeType.TXT, "abc");
+ val representation = representationFor(value, osObj->assertEquals(
+ JsonRepresentation.newMap("name", "a Clob.txt",
+ "mimeType", "text/plain",
+ "chars", "abc").toString(),
+ osObj.toString()));
+
+ assertThat(representation.getString("extensions.x-isis-format"),
is("clob"));
+ }
+
+ static enum SampleEnum {
+ HALLO,
+ WORLD
+ }
+
+ @Test
+ void whenEnum() {
+ val value = SampleEnum.HALLO;
+ val representation = representationFor(value, osObj->assertEquals(
+ JsonRepresentation.newMap("enumType",
SampleEnum.class.getName(),
+ "enumName", value.name()).toString(),
+ osObj.toString()));
+
+ assertThat(representation.getString("extensions.x-isis-format"),
is("enum"));
}
private JsonRepresentation representationFor(final Object value) {
@@ -395,6 +426,9 @@ class JsonValueEncoderTest {
val representation = JsonRepresentation.newMap();
jsonValueEncoder.appendValueAndFormat(valueAdapter, representation,
context);
+
+ //debug
+ //System.err.printf("value %s-> %s %n",
valueAdapter.getSpecification().getCorrespondingClass(), representation);
return representation;
}
diff --git
a/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
b/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
index 3d619d7ca4..62e1d5b1ab 100644
---
a/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
+++
b/viewers/restfulobjects/applib/src/main/java/org/apache/isis/viewer/restfulobjects/applib/JsonRepresentation.java
@@ -48,12 +48,17 @@ import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.springframework.lang.Nullable;
+import org.apache.isis.commons.internal.base._Casts;
import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.resources._Json;
import org.apache.isis.viewer.restfulobjects.applib.util.JsonNodeUtils;
import org.apache.isis.viewer.restfulobjects.applib.util.PathNode;
import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils;
+import lombok.val;
+
/**
* A wrapper around {@link JsonNode} that provides some additional helper
* methods.
@@ -120,6 +125,19 @@ public class JsonRepresentation {
return transformer;
}
+ public static JsonRepresentation jsonAsMap(final @Nullable String
keyValuePairsAsJson) {
+ val repr = JsonRepresentation.newMap();
+ if(_Strings.isNotEmpty(keyValuePairsAsJson)) {
+ final Map<Object, Object> keyValuePairs = _Casts.uncheckedCast(
+ _Json.readJson(Map.class,
keyValuePairsAsJson).getValue().orElseThrow());
+
+ keyValuePairs.forEach((key, value)->{
+ repr.mapPutString(""+key, ""+value);
+ });
+ }
+ return repr;
+ }
+
public static JsonRepresentation newMap(final String... keyValuePairs) {
final JsonRepresentation repr = new JsonRepresentation(new
ObjectNode(JsonNodeFactory.instance));
String key = null;
@@ -1759,9 +1777,7 @@ public class JsonRepresentation {
}
- // ///////////////////////////////////////////////////////////////////////
- // equals and hashcode
- // ///////////////////////////////////////////////////////////////////////
+ // -- OBJECT CONTRACT (NO SEMANTIC EQUALITY) ... use toString() to check
for semantic equality
@Override
public int hashCode() {
@@ -1782,15 +1798,12 @@ public class JsonRepresentation {
JsonRepresentation other = (JsonRepresentation) obj;
if (jsonNode == null) {
if (other.jsonNode != null)
- return false;
+ return false;
} else if (!jsonNode.equals(other.jsonNode))
return false;
return true;
}
- // ///////////////////////////////////////////////////////////////////////
- // toString
- // ///////////////////////////////////////////////////////////////////////
@Override
public String toString() {
diff --git
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
index 3def057737..fa6b7d59dd 100644
---
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
+++
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueConverters.java
@@ -30,7 +30,6 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
-import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.core.metamodel.spec.ManagedObject;
import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
@@ -40,10 +39,8 @@ import lombok.val;
/**
* Has support for JSON primitives.
- * @deprecated should be covered 100% per {@link ValueSemanticsProvider}
*/
-@Deprecated
-public final class JsonValueConverters {
+final class JsonValueConverters {
@RequiredArgsConstructor
public static enum DefaultFormat {
@@ -56,14 +53,24 @@ public final class JsonValueConverters {
FLOAT(Float.class, "decimal", "float"),
DOUBLE(Double.class, "decimal", "double"),
CHAR(Character.class, null, "char"),
+
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
BIGINTEGER(BigInteger.class, "big-integer(18)", "javamathbiginteger"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
BIGDECIMAL(BigDecimal.class, "big-decimal", "javamathbigdecimal"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JODALOCALDATE(LocalDate.class, "date", "jodalocaldate"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JODALOCALDATETIME(LocalDateTime.class, "date-time",
"jodalocaldatetime"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JODADATETIME(DateTime.class, "date-time", "jodadatetime"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JAVAUTILDATE(java.util.Date.class, "date-time", "javautildate"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JAVASQLDATE(java.sql.Date.class, "date", "javasqldate"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JAVASQLTIME(java.sql.Time.class, "time", "javasqltime"),
+ @Deprecated //should be covered 100% per {@link ValueSemanticsProvider}
JAVASQLTIMESTAMP(java.sql.Timestamp.class, "utc-millisec",
"javasqltimestamp"),
;
diff --git
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
index 22f0513b76..f49c5ee5b3 100644
---
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
+++
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/JsonValueEncoder.java
@@ -20,6 +20,7 @@ package
org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Priority;
@@ -44,6 +45,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObjects;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.metamodel.util.Facets;
+import org.apache.isis.schema.common.v2.ValueType;
import
org.apache.isis.viewer.restfulobjects.applib.IsisModuleViewerRestfulObjectsApplib;
import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
import
org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueConverter.Context;
@@ -122,7 +124,7 @@ public class JsonValueEncoder {
throw new IllegalArgumentException("Could not parse value '" +
argValueRepr.asString() + "' as a " + objectSpec.getFullIdentifier());
}
- public Object appendValueAndFormat(
+ public void appendValueAndFormat(
final ManagedObject valueAdapter,
final JsonRepresentation repr,
final Context context) {
@@ -131,17 +133,43 @@ public class JsonValueEncoder {
val valueClass = valueSpec.getCorrespondingClass();
val jsonValueConverter = converterByClass.get(valueClass);
if(jsonValueConverter != null) {
- return jsonValueConverter.appendValueAndFormat(valueAdapter,
context, repr);
+ jsonValueConverter.appendValueAndFormat(valueAdapter, context,
repr);
+ return;
} else {
final Optional<ValueDecomposition> valueDecompositionIfAny =
decompose(valueAdapter);
-
if(valueDecompositionIfAny.isPresent()) {
- val value = valueDecompositionIfAny.get().toJson();
- repr.mapPutString("value", value);
- appendFormats(repr, "string", "string",
context.isSuppressExtensions());
- return value;
+ val valueDecomposition = valueDecompositionIfAny.get();
+ val valueAsJson = valueDecomposition.toJson();
+ valueDecomposition.accept(
+ simple->{
+ // special treatment for BLOB/CLOB/ENUM as these
are better represented by a map
+ if(simple.getType() == ValueType.BLOB
+ || simple.getType() == ValueType.CLOB
+ || simple.getType() == ValueType.ENUM) {
+ val decompRepr =
JsonRepresentation.jsonAsMap(valueAsJson);
+ repr.mapPutJsonRepresentation("value",
decompRepr);
+ appendFormats(repr, null,
simple.getType().value(), context.isSuppressExtensions());
+ } else {
+ // using string representation from value
semantics
+ repr.mapPutString("value", valueAsJson);
+ appendFormats(repr, "string",
simple.getType().value(), context.isSuppressExtensions());
+ }
+ },
+ tuple->{
+ val decompRepr =
JsonRepresentation.jsonAsMap(valueAsJson);
+ repr.mapPutJsonRepresentation("value", decompRepr);
+
+ val typeTupleAsFormat = "{"
+ + tuple.getElements().stream()
+ .map(el->el.getType().value())
+ .collect(Collectors.joining(","))
+ + "}";
+
+ appendFormats(repr, null, typeTupleAsFormat,
context.isSuppressExtensions());
+ });
+ } else {
+ appendNullAndFormat(repr, context.isSuppressExtensions());
}
- return appendNullAndFormat(repr, context.isSuppressExtensions());
}
}
@@ -188,13 +216,11 @@ public class JsonValueEncoder {
.toEncodedString(Format.JSON,
_Casts.uncheckedCast(adapter.getPojo()));
}
- @Deprecated
- static void appendFormats(final JsonRepresentation repr, final String
format, final String xIsisFormat, final boolean suppressExtensions) {
- if(format != null) {
- repr.mapPutString("format", format);
- }
- if(!suppressExtensions && xIsisFormat != null) {
- repr.mapPutString("extensions.x-isis-format", xIsisFormat);
+ static void appendFormats(final JsonRepresentation repr,
+ final @Nullable String format, final @Nullable String
extendedFormat, final boolean suppressExtensions) {
+ repr.putFormat(format);
+ if(!suppressExtensions) {
+ repr.putExtendedFormat(extendedFormat);
}
}
diff --git
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
index cddfeeb9ab..2d850dc881 100644
---
a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
+++
b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/domainobjects/ObjectPropertyReprRenderer.java
@@ -78,7 +78,7 @@ extends AbstractObjectMemberReprRenderer<OneToOneAssociation>
{
// value
// ///////////////////////////////////////////////////
- private Object addValue(final LinkFollowSpecs linkFollower) {
+ private void addValue(final LinkFollowSpecs linkFollower) {
val valueAdapterIfAny = objectMember.get(objectAdapter,
getInteractionInitiatedBy());
// use the runtime type if we have a value, otherwise fallback to the
compile time type of the member
@@ -89,17 +89,18 @@ extends
AbstractObjectMemberReprRenderer<OneToOneAssociation> {
val spec = valueAdapter.getSpecification();
if (spec.isValue()) {
- return jsonValueEncoder
+ jsonValueEncoder
.appendValueAndFormat(
valueAdapter,
representation,
JsonValueConverter.Context.of(objectMember,
resourceContext.suppressMemberExtensions()));
+ return;
}
if(valueAdapter.getPojo() == null) {
final NullNode value = NullNode.getInstance();
representation.mapPutJsonNode("value", value);
- return value;
+ return;
}
final boolean eagerlyRender =
@@ -125,7 +126,6 @@ extends
AbstractObjectMemberReprRenderer<OneToOneAssociation> {
final JsonRepresentation valueJsonRepr = valueLinkBuilder.build();
representation.mapPutJsonRepresentation("value", valueJsonRepr);
- return valueJsonRepr;
}