Skip to content

Commit 3d48c3d

Browse files
committed
deserialization now also works
1 parent 7d690b7 commit 3d48c3d

File tree

5 files changed

+320
-100
lines changed

5 files changed

+320
-100
lines changed
Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,120 @@
11
package org.tensorics.gson.adapters;
22

33
import com.google.common.annotations.VisibleForTesting;
4-
import com.google.common.collect.Iterables;
54
import com.google.gson.Gson;
65
import com.google.gson.TypeAdapter;
6+
import com.google.gson.internal.JsonReaderInternalAccess;
77
import com.google.gson.reflect.TypeToken;
88
import com.google.gson.stream.JsonReader;
99
import com.google.gson.stream.JsonWriter;
1010
import org.tensorics.core.lang.Tensorics;
11+
import org.tensorics.core.tensor.Position;
1112
import org.tensorics.core.tensor.Tensor;
12-
import org.tensorics.core.tensor.operations.TensorInternals;
1313
import org.tensorics.core.tensorbacked.Tensorbacked;
1414
import org.tensorics.core.tensorbacked.TensorbackedInternals;
15+
import org.tensorics.gson.util.Nestmaps;
1516

1617
import java.io.IOException;
18+
import java.util.HashMap;
1719
import java.util.List;
1820
import java.util.Map;
1921

2022
import static java.util.Objects.requireNonNull;
23+
import static org.tensorics.core.tensorbacked.TensorbackedInternals.valueTypeFrom;
2124

2225
public class TensorbackedGsonAdapter<V, TB extends Tensorbacked<V>> extends TypeAdapter<TB> {
2326

2427
private final Gson context;
2528
private final Class<TB> tensorbackedClass;
2629

30+
private final TypeAdapter<V> valueAdapter;
31+
2732
public TensorbackedGsonAdapter(Gson context, Class<TB> tensorbackedClass) {
2833
this.context = context;
2934
this.tensorbackedClass = requireNonNull(tensorbackedClass, "tensorbackedClass must not be null.");
35+
36+
this.valueAdapter = adapterFor(valueTypeFrom(tensorbackedClass));
3037
}
3138

3239
@Override
3340
public void write(JsonWriter out, TB value) throws IOException {
3441
/*XXX: The context of the tensor will currently NOT be serialized! */
3542

3643
List<Class<?>> dimensions = TensorbackedInternals.dimensionListFrom(tensorbackedClass);
37-
Object nested = nestmap(value.tensor(), dimensions);
44+
Object nested = Nestmaps.nestmap(value.tensor(), dimensions);
3845
if (nested instanceof Map) {
3946
TypeAdapter<Map<?, ?>> adapter = context.getAdapter(new TypeToken<Map<?, ?>>() {
4047
});
4148
adapter.write(out, (Map<?, ?>) nested);
4249
} else { /* This is the special case of a scalar */
43-
Class<V> valueType = TensorbackedInternals.valueTypeFrom(tensorbackedClass);
44-
TypeAdapter<V> adapter = context.getAdapter(TypeToken.get(valueType));
45-
adapter.write(out, (V) nested);
50+
valueAdapter.write(out, (V) nested);
4651
}
4752
}
4853

54+
@Override
55+
public TB read(JsonReader in) throws IOException {
56+
List<Class<?>> dimensions = TensorbackedInternals.dimensionListFrom(tensorbackedClass);
57+
Object object = recursiveRead(in, dimensions);
58+
Tensor<V> unnested = Nestmaps.unnestmap(object, dimensions);
59+
return TensorbackedInternals.createBackedByTensor(tensorbackedClass, unnested);
60+
}
61+
4962
@VisibleForTesting
50-
static Object nestmap(Tensor<?> tensor, List<Class<?>> dimensions) {
51-
int tensorDimensionality = Tensorics.dimensionsOf(tensor).size();
52-
if (tensorDimensionality != dimensions.size()) {
53-
throw new IllegalArgumentException("Tensor dimensionality (" + tensorDimensionality +
54-
") and number of provided dimensions (" + dimensions.size() + ": " + dimensions +
55-
") do not match!");
63+
Object recursiveRead(JsonReader in, List<Class<?>> dimensions) throws IOException {
64+
if (dimensions.isEmpty()) {
65+
/* This is the special case of a scalar and the final value */
66+
return valueAdapter.read(in);
67+
} else {
68+
Class<?> thisDim = dimensions.get(0);
69+
List<Class<?>> remainingDims = dimensions.subList(1, dimensions.size());
70+
return readMap(in, thisDim, remainingDims);
5671
}
72+
}
5773

58-
if (dimensions.isEmpty()) {
59-
return Tensorics.from(tensor).optional().orElse(null);
74+
private <T> Map<T, Object> readMap(JsonReader in, Class<T> keyDim, List<Class<?>> remainingDimensions) throws IOException {
75+
TypeAdapter<T> dimAdapter = adapterFor(keyDim);
76+
Map<T, Object> map = new HashMap<>();
77+
78+
in.beginObject();
79+
while (in.hasNext()) {
80+
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
81+
T key = dimAdapter.read(in);
82+
Object value = recursiveRead(in, remainingDimensions);
83+
map.put(key, value);
6084
}
85+
in.endObject();
86+
87+
return map;
88+
}
6189

62-
Class<?> dimension = Iterables.getLast(dimensions);
63-
Tensor<? extends Map<?, ?>> mappedOut = TensorInternals.mapOut(tensor).inDirectionOf(dimension);
6490

65-
List<Class<?>> remainingDimensions = dimensions.subList(0, dimensions.size() - 1);
66-
return nestmap(mappedOut, remainingDimensions);
91+
private <T> TypeAdapter<T> adapterFor(Class<T> valueType) {
92+
return context.getAdapter(TypeToken.get(valueType));
6793
}
6894

69-
@Override
70-
public TB read(JsonReader in) throws IOException {
95+
96+
/*
97+
JsonToken peek = in.peek();
98+
if (peek == JsonToken.NULL) {
99+
in.nextNull();
71100
return null;
72-
}
101+
}
102+
103+
Map<K, V> map = constructor.construct();
104+
105+
if (peek == JsonToken.BEGIN_ARRAY) {
106+
in.beginArray();
107+
while (in.hasNext()) {
108+
in.beginArray(); // entry array
109+
K key = keyTypeAdapter.read(in);
110+
V value = valueTypeAdapter.read(in);
111+
V replaced = map.put(key, value);
112+
if (replaced != null) {
113+
throw new JsonSyntaxException("duplicate key: " + key);
114+
}
115+
in.endArray();
116+
}
117+
in.endArray();
118+
}
119+
*/
73120
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.tensorics.gson.util;
2+
3+
import com.google.common.collect.Iterables;
4+
import org.tensorics.core.lang.Tensorics;
5+
import org.tensorics.core.tensor.Position;
6+
import org.tensorics.core.tensor.Positions;
7+
import org.tensorics.core.tensor.Tensor;
8+
import org.tensorics.core.tensor.TensorBuilder;
9+
import org.tensorics.core.tensor.operations.TensorInternals;
10+
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
/**
15+
* Contains utility methods to convert tensors into nested maps and vice versa.
16+
*/
17+
public class Nestmaps {
18+
19+
private Nestmaps() {
20+
throw new UnsupportedOperationException("Only static methods");
21+
}
22+
23+
/**
24+
* Creates a map of map of ... from the given tensor. If the passed in tensor is a scalar (tensor with dimension 0),
25+
* then a simple value is returned. If the tensor is empty, then null is returned.
26+
*
27+
* @param tensor the tensor to convert into a nested map
28+
* @param dimensions the dimensions of the tensor. The first value in there will be the top level key type,
29+
* the second the next level, etc.
30+
* @return a nested map, or a value if the passed in value is a scalar.
31+
*/
32+
public static Object nestmap(Tensor<?> tensor, List<Class<?>> dimensions) {
33+
int tensorDimensionality = Tensorics.dimensionsOf(tensor).size();
34+
if (tensorDimensionality != dimensions.size()) {
35+
throw new IllegalArgumentException("Tensor dimensionality (" + tensorDimensionality +
36+
") and number of provided dimensions (" + dimensions.size() + ": " + dimensions +
37+
") do not match!");
38+
}
39+
40+
if (dimensions.isEmpty()) {
41+
return Tensorics.from(tensor).optional().orElse(null);
42+
}
43+
44+
Class<?> dimension = Iterables.getLast(dimensions);
45+
Tensor<? extends Map<?, ?>> mappedOut = TensorInternals.mapOut(tensor).inDirectionOf(dimension);
46+
47+
List<Class<?>> remainingDimensions = dimensions.subList(0, dimensions.size() - 1);
48+
return nestmap(mappedOut, remainingDimensions);
49+
}
50+
51+
public static <T> Tensor<T> unnestmap(Object in, List<Class<?>> dimensions) {
52+
TensorBuilder<T> builder = Tensorics.builder(dimensions);
53+
unnestmap(in, Position.of(), builder);
54+
return builder.build();
55+
}
56+
57+
public static <T> void unnestmap(Object remainingIn, Position pos, TensorBuilder<T> builder) {
58+
if (remainingIn instanceof Map) {
59+
Map<?, ?> map = (Map<?, ?>) remainingIn;
60+
for (Map.Entry<?, ?> entry : map.entrySet()) {
61+
Position newPos = Positions.union(pos, Position.of(entry.getKey()));
62+
unnestmap(entry.getValue(), newPos, builder);
63+
}
64+
} else {
65+
builder.put(pos, (T) remainingIn);
66+
}
67+
}
68+
69+
}

src/test/java/org/tensorics/gson/adapters/NestmapTest.java

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)