1

In this question we see that children element can either be an array of additional data items or a boolean

I am using Java to create the data, store in a Data class and then convert to Json using Google Gson. But because children can be two different things I have represented this by having a variable kids in my Data that I use when I need to store as boolean. Then do a String replace on the resulting json to turn it into a children element.

But this hack is not very satisfactory and problematic (i.e if they have real data with content "kids") so what is the proper way to represent data with varying data types.

public class Data
{
    private String id;
    private String text;
    private String icon;
    private State  state;
    private boolean kids;

    private List<Data> children = null;

    public String getId()
    {
        return id;
    }

    public void setId(String id)
    {
        this.id = id;
    }

    public String getText()
    {
        return text;
    }

    public void setText(String text)
    {
        this.text = text;
    }

    public String getIcon()
    {
        return icon;
    }

    public void setIcon(String icon)
    {
        this.icon = icon;
    }

    public List<Data> getChildren()
    {
        return children;
    }

    public void setChildren(List<Data> children)
    {
        this.children = children;
    }

    public State getState()
    {
        return state;
    }

    public void setState(State state)
    {
        this.state = state;
    }

    public boolean getKids()
    {
        return kids;
    }

    public void setKids(boolean kids)
    {
        this.kids = kids;
    }
}

public static String createFolderJsonData()
{
  CreateFolderTree cft = new CreateFolderTree(null);
  String treeData = cft.start(1).replace("kids", "children");
  return treeData;
}
2
  • So the list can either be Data or Boolean? And what does getKids() return? Commented Jan 31, 2018 at 12:17
  • No, the json Im trying to represent can be either a 'list of Data', or a 'Boolean' (not a list of Booleans). SInce I cannot see a way to do that I use children variable for the list and the kids variable for the boolean, but the resultant json is meant to just have the two different versions of children, linked question explains clearly the output. Commented Jan 31, 2018 at 12:23

2 Answers 2

1

Assuming that you know what type the child is you can solve it like this

public class Data<T>{

    private T children = null;

    public T getChildren(){
        return children;
    }

    public void setChildren(T children){
        this.children = children;
    }
}

public class DataRunner {
    public static void main(String[] args){
        Data<List<Data>> data = new Data<>();

        Data<List<Data>> subDataOne = new Data<>();
        subDataOne.setChildren(new ArrayList<>());

        Data<Boolean> subDataTwo = new Data<>();
        subDataTwo.setChildren(true);

        List<Data> listData = new ArrayList<>();

        listData.add(subDataOne);
        listData.add(subDataTwo);

        data.setChildren(listData);

        // {"children":[{"children":[]},{"children":true}]}
        System.out.println(new Gson().toJson(data));
    }
}

Now it prints the correct thing, notice that the ArrayList doesn't care what generic type Data has

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks but I dont understand your example I'm generating json from the Data class but in main you appear to be doing the opposite.
My bad, it's the same principle just opposite
Just to confirm I have now actually implemented this fix and it worked for me
0

For the deserialization :

You have to make two classes, one for the Boolean type (I named it BoolData) and one for the List<Data> type (ListData). Then you can write a custom JsonDeserializer who will deserialize the object as BoolData or ListData, using the JSON value to determine which type to use.

Model :

public abstract class Data<T> {

    private String id;

    private T children;

    // ...
    public Data() {
    }

    public String getId() {
        return id;
    }

    public T getChildren() {
        return children;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setChildren(T children) {
        this.children = children;
    }

    @Override
    public String toString() {
        return "Data{" + "id=" + id + ", children=" + children + '}';
    }
}

public class ListData extends Data<List<Data<?>>> {
}

public class BoolData extends Data<Boolean> {
}

Deserializer :

String json = ""
        + "["
        + "  {"
        + "    \"id\": \"1\""
        + "  },"
        + "  {"
        + "    \"id\": \"2\","
        + "    \"children\": ["
        + "       {"
        + "         \"id\": \"2.1\","
        + "         \"children\": true"
        + "       },"
        + "       {"
        + "         \"id\": \"2.2\","
        + "         \"children\": []"
        + "       }"
        + "    ]"
        + "  }"
        + "]";
    List<Data<?>> data = new GsonBuilder()
        .registerTypeAdapter(Data.class, (JsonDeserializer<Data<?>>) (elem, type, ctx) -> {
            JsonObject obj = elem.getAsJsonObject();
            JsonElement children = obj.get("children");

            // If "children" is a boolean, deserialize it as BoolData
            return children != null && children.isJsonPrimitive() && children.getAsJsonPrimitive().isBoolean()
                ? ctx.deserialize(elem, BoolData.class)
                : ctx.deserialize(elem, ListData.class);
        })
        .create()
        .fromJson(json, new TypeToken<List<Data<?>>>() {
        }.getType());
    System.out.println(data);

Output :

[Data{id=1, children=null}, Data{id=2, children=[Data{id=2.1, children=true}, Data{id=2.2, children=[]}]}]

JSON used :

[
    {
        "id": "1"
    },
    {
        "id": "2",
        "children": [
            {
                "id": "2.1",
                "children": true
            }, {
                "id": "2.2",
                "children": []
            }
        ]
    }
]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.