Skip to content
Permalink
Browse files

workaround for serialization of incorrect collections and maps

Found an example of collection which throws UnsupportedOperationException
in iterator(). Such incorrect collections cannot be serialized as JSON
arrays, but only "as objects". So, a workaround is implemented which
checks if iterator() works before deciding whether to serialize
"as collection" or "as object". Same is done for maps
  • Loading branch information...
amogilev committed Jun 9, 2019
1 parent 883e075 commit 4068903c8822b775f5701b17008e67b39ae030c4
@@ -450,14 +450,29 @@ public void write(JsonWriter out, Collection<E> collection, WriteContext ctx) th
return;
}

out.beginArray();
// NOTE: serializing "as collection" requires call of custom class-provided methods: Collection.iterator()
// and the methods of that iterator, which is not perfectly safe. There are examples where iterator()
// throws UnsupportedOperationException, and potentially hasNext()/next() may throw exceptions too.
// Currently only iterator() is checked before decision to use "as collection" vs. "as object"
// serialization, but if more examples will be found, it would be more safe to copy everything to a
// temporal ArrayList and use "as object" if failed.
Iterator<E> it;
try {
it = collection.iterator();
} catch (Exception e) {
AdapterUtils.writeByReflectiveAdapter(out, collection, ctx, gson, formalCollType);
return;
}

out.beginArray();
int i = writeExtraFields(collection, out, ctx);

for (E element : collection) {
while (it.hasNext()) {
E element = it.next();
ctx.doWrite(element, elementTypeAdapter, Integer.toString(i), out);
i++;
}

out.endArray();
}
}
@@ -447,27 +447,42 @@ public void write(JsonWriter out, Map<K, V> map, WriteContext ctx) throws IOExce
return;
}

boolean skipEntries = ctx.isSkipNextMapEntries();
Iterator<Map.Entry<K, V>> it = null;
if (!skipEntries) {
try {
it = map.entrySet().iterator();
} catch (Exception e) {
// incorrect map implementation - revert to "as object" serialization
AdapterUtils.writeByReflectiveAdapter(out, map, ctx, gson, formalMapType);
return;
}
}

if (!complexMapKeySerialization) {
out.beginObject();
int i = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
out.name(String.valueOf(entry.getKey()));
ctx.doWrite(entry.getValue(), valueTypeAdapter, "" + i + "-val", out);
i++;
if (!skipEntries) {
while (it.hasNext()) {
Map.Entry<K, V> entry = it.next();
out.name(String.valueOf(entry.getKey()));
ctx.doWrite(entry.getValue(), valueTypeAdapter, "" + i + "-val", out);
i++;
}
}
writeExtraFields(out, map, ctx, i, false);
out.endObject();
return;
}

boolean skipEntries = ctx.isSkipNextMapEntries();
boolean hasComplexKeys = false;
List<JsonElement> keys = new ArrayList<JsonElement>(map.size());
List<V> values = new ArrayList<V>(map.size());

if (!skipEntries) {
int i = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
while (it.hasNext()) {
Map.Entry<K, V> entry = it.next();
JsonElement keyElement = ctx.doToJsonTree(entry.getKey(), keyTypeAdapter, keyRef(i));
i++;
keys.add(keyElement);
@@ -0,0 +1,65 @@
package com.gilecode.yagson.tests;

import com.gilecode.yagson.YaGson;
import com.gilecode.yagson.tests.util.BindingTestCase;

import java.util.*;

public class TestIncorrectCollections extends BindingTestCase {

static class IncorrectSet extends AbstractSet<String> {

@Override
public Iterator<String> iterator() {
throw new UnsupportedOperationException();
}

@Override
public int size() {
return 1;
}
}

static class IncorrectMap extends AbstractMap<String, String> {

@Override
public Set<Entry<String, String>> entrySet() {
throw new UnsupportedOperationException();
}

@Override
public int size() {
return 1;
}

@Override
public String put(String key, String value) {
return super.put(key, value);
}
}


public void testIncorrectSet() {
YaGson gson = new YaGson();
Set<String> obj = new IncorrectSet();
String str = gson.toJson(obj);
assertEquals(
jsonStr("{'@type':'com.gilecode.yagson.tests.TestIncorrectCollections$IncorrectSet','@val':{}}"),
str);
Set obj2 = gson.fromJson(str, Set.class);
assertTrue(obj2 instanceof IncorrectSet);
}

public void testIncorrectMap() {
YaGson gson = new YaGson();
Map<String, String> obj = new IncorrectMap();
String str = gson.toJson(obj);
assertEquals(
jsonStr("{'@type':'com.gilecode.yagson.tests.TestIncorrectCollections$IncorrectMap','@val':{}}"),
str);
Map obj2 = gson.fromJson(str, Map.class);
assertTrue(obj2 instanceof IncorrectMap);
}


}

0 comments on commit 4068903

Please sign in to comment.
You can’t perform that action at this time.