1

I want to implement a group of generic functions named toJson() to convert objects to JSON strings. Very similar to this post, How to symmetrically implement serialize and deserialize template functions in C++

For now I only need to support the following types boolean, double, int ,long, String , array and ArrayList, and allow arbitrary combinations such as int[][][][][], ArrayList<ArrayList<int[]>>, etc.

Serializer.java:

import java.util.ArrayList;

public interface Serializer {
    static String toJson(boolean value) {
        return String.valueOf(value);
    }
    static String toJson(double value) {
        return String.valueOf(value);
    }
    static String toJson(int value) {
        return String.valueOf(value);
    }
    static String toJson(long value) {
        return String.valueOf(value);
    }
    static String toJson(final String value) {
        return "\"" + value + "\"";
    }

    static <T> String toJson(final T value) {
        return String.valueOf(value);
    }

    static <T>  String toJson(final T[] value) {
        final StringBuilder result = new StringBuilder("[");

        for (final T i : value) {
            result.append(toJson(i));
            result.append(',');
        }

        if (value.length > 0) result.delete(result.length() - 1, result.length());

        result.append(']');

        return result.toString();
    }

    static <T>  String toJson(final ArrayList<T> value) {
        final StringBuilder result = new StringBuilder("[");

        for (final T i : value) {
            result.append(toJson(i));
            result.append(',');
        }

        if (value.size() > 0) result.delete(result.length() - 1, result.length());

        result.append(']');

        return result.toString();
    }
}

Unit tests:

import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;


public class SerializerTest {

    @Test public void primitiveToJsonTest() {
        assertEquals("true", Serializer.toJson(true));
        assertEquals("123.0", Serializer.toJson(123.000));
        assertEquals("123", Serializer.toJson(123));
        assertEquals("123", Serializer.toJson(123L));
        assertEquals("\"123\"", Serializer.toJson("123"));
    }

    @Test public void containerToJsonTest() {
        System.out.println(Serializer.toJson(new int[]{1,2,3,4,5})); // not correct !
        assertEquals("[1, 2, 3, 4, 5]", Serializer.toJson(Arrays.asList(1,2,3,4,5)));
        assertEquals("[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]", 
            Serializer.toJson(Arrays.asList(Arrays.asList(1,2,3,4,5), Arrays.asList(6,7,8,9, 10))));
    }
}

The only problem is about arrays, it seems Java will treat T[] as T.

Any ideas?

Community
  • 1
  • 1
soulmachine
  • 3,917
  • 4
  • 46
  • 56
  • First of all, generic arrays in Java are weird. Secondly, method dispatch in Java isn't dependent on the actual type of the method parameter, only the formal type. So all your array elements will be serialized with `static String toJson(final T value)`, except strings, which will probably cause a compile error due to ambiguous signatures. – biziclop Sep 24 '15 at 09:12
  • this code compiles, actually `toJson(final String value)` will never get called by ` toJson(final T[] value)` or `toJson(final ArrayList value)`, both of them will always call `toJson(final T value)` – soulmachine Sep 24 '15 at 22:09
  • Okay, I finally get what your intention was, I completely misunderstood it at first. Sorry. :) – biziclop Sep 24 '15 at 22:38

2 Answers2

1

The array case you have implemented only covers object arrays (e.g. Integer[]), you'll need to handle int[] and other primitive array types separately -- just like you handle int etc.

This is a problem only for the first level. In the case of int[][] etc., the "outer" array is an object array, as int[] is an Object so it's sufficient to cover one level.

Stefan Haustein
  • 18,427
  • 3
  • 36
  • 51
  • How to support multiple dimensional arrays? For example, `int[][][][][][]`, the C++ code can supports this, how does Java achieve this? – soulmachine Sep 24 '15 at 17:15
  • int[] is an Object, so it's fine, you only need to cover one level. Have edited the answer to better clarify this – Stefan Haustein Sep 24 '15 at 17:45
  • But the `ArrayList` works fine , the code above supports any level of `ArrayList`, it seems the Java compiler will treat `T[]` as `Object[]` so it will not infer the right type for elements in an array, but it will infer the element type for `ArrayList`, weird... – soulmachine Sep 24 '15 at 18:13
  • Well you can't have ArrayList, only ArrayList or ArrayList, and Integer[] works, but you want to get int[] working. Arrays of primitive type are objects, but not object arrays, that's the problem here. – Stefan Haustein Sep 24 '15 at 19:22
1

Only reference types can be generic, for example, C<T>, both C and T should be reference types.

if T[] matches to int[], then T will be int which is a primitive type, so T[] won't match int[], instead it will match T. This is the reason

Serializer.toJson(new int[]{1,2,3,4,5})

will call static <T> String toJson(final T value)

So the right way is to change new int[]{1,2,3,4,5} to new Integer[]{1,2,3,4,5}), since Integer is a reference type, Integer[] can match to T[].

By only using templates, you're not able to write toJson() like in C++, because C++'s template is more powerful at compile time. You have to use both templates and reflection to achieve the same goal.

soulmachine
  • 3,917
  • 4
  • 46
  • 56