简体   繁体   中英

How to sum values of jsonObject and create new jsonobject of sum inside a jsonArray in JAVA?

I have JSON Array as below and want to sum values of JSON object and create new JSON object of sum inside a JSON array:

{
   "client":[
      {
         "member":12,
         "group":"g1"
      },
      {
         "member":17,
         "group":"g2"
      }
   ],
   "client2":[
      {
         "member":14,
         "group":"g11"
      },
      {
         "member":175,
         "group":"g22"
      }
   ]
}

I want to sum the member value for each jsonobject inside the jsonarray and create extra json and put it inside client array. The expected json should look like below:

{
   "client":[
      {
         "member":12,
         "group":"g1"
      },
      {
         "member":17,
         "group":"g2"
      },
      {
         "totalMember":29,
         "group":"all"
      }
   ],
   "client2":[
      {
         "member":14,
         "group":"g11"
      },
      {
         "member":175,
         "group":"g22"
      },
      {
         "totalMember":189,
         "group":"all"
      }
   ]
}

I tried as:

    mainJson.fieldNames().forEach(fn->{
      JsonArray jsonArray  = mainJson.getJsonArray(fn);
      int id = 0;
      for (int i = 0; i < jsonArray.size(); i++) {
          id += jsonArray.getJsonObject(i).getInteger("id");
        JsonObject jsonObject = new JsonObject().put("id",id).put("group","all");
        jsonArray.add(jsonObject);
        mainJson.put(fn,jsonArray);
      }
    });

So, below is a full worked example using gson library (googles json parser).

First i created the class for defining the initial json file:

import java.io.Serializable;
import java.util.ArrayList;

public class ClientSer implements Serializable {
    ArrayList<ClientDataSer> client;
    ArrayList<ClientDataSer> client2;

    public ClientSer(ArrayList<ClientDataSer> client, ArrayList<ClientDataSer> client2) {
        this.client = client;
        this.client2 = client2;
    }

    public ArrayList<ClientDataSer> getClient() {
        return client;
    }

    public void setClient(ArrayList<ClientDataSer> client) {
        this.client = client;
    }

    public ArrayList<ClientDataSer> getClient2() {
        return client2;
    }

    public void setClient2(ArrayList<ClientDataSer> client2) {
        this.client2 = client2;
    }
}

With client data ser looking like:

public class ClientDataSer extends ClientDataParentSer {
    int member;

    public ClientDataSer(int member, String group) {
        super(group);
        this.member = member;
    }

    public int getMember() {
        return member;
    }

    public void setMember(int member) {
        this.member = member;
    }

}

In order for gson to uses files as definitions of data structure, they need to be serialisable. I will get the why ClientDataSer extends ClientDataParentSer in a moment.

The code for reading this file, caluclating the total member value and printing it to another file is shown below:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.*;
import java.util.ArrayList;

public class Main {

    public static void main(String[] args) {


        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();

        try (Reader reader = new FileReader("test.json")) {

            // Convert JSON File to Java Object
            ClientSer clientSer = gson.fromJson(reader, ClientSer.class);

            ClientNewSer clientNewSer = new ClientNewSer(getNewClientData(clientSer.getClient()), getNewClientData(clientSer.getClient2()));

            try {
                Writer writer = new FileWriter("testNew.json");
                gson.toJson(clientNewSer, writer);
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static ArrayList<ClientDataParentSer> getNewClientData(ArrayList<ClientDataSer> clientDataSerList) {

        ArrayList<ClientDataParentSer> clientDataSers = new ArrayList<>();

        int memberCounter = 0;
        for (ClientDataParentSer clientDataSer : clientDataSerList) {

            clientDataSers.add(clientDataSer);

            memberCounter += ((ClientDataSer)clientDataSer).getMember();
        }

        ClientDataNewSer clientDataNewSer = new ClientDataNewSer("all", memberCounter);

        clientDataSers.add(clientDataNewSer);

        return clientDataSers;

    }
}

So, as you wanted client and client2 to contain a list each with 2 different obejects (one with field member and group, and the other with fields total member and group), we had to do some hierarchy stuff.

If we make a parent class containing the common field (group):

import java.io.Serializable;

public class ClientDataParentSer implements Serializable {


    private final String group;

    public ClientDataParentSer(String group) {
        this.group = group;
    }

    public String getGroup() {
        return group;
    }
}

and then make ClientDataSer and a new class:

public class ClientDataNewSer extends ClientDataParentSer {

    int member;

    public ClientDataNewSer(String group, int member) {
        super(group);
        this.member = member;
    }

    public int getMember() {
        return member;
    }

    public void setMember(int member) {
        this.member = member;
    }
}

extend this parent class, we can have a list of ClientDataParentSer that contain both, ie the list the output json file needs.

the class for the new object is shown below:

import java.io.Serializable;
import java.util.ArrayList;

public class ClientNewSer implements Serializable {
    ArrayList<ClientDataParentSer> client;
    ArrayList<ClientDataParentSer> client2;

    public ClientNewSer(ArrayList<ClientDataParentSer> client, ArrayList<ClientDataParentSer> client2) {
        this.client = client;
        this.client2 = client2;
    }

    public ArrayList<ClientDataParentSer> getClient() {
        return client;
    }

    public void setClient(ArrayList<ClientDataParentSer> client) {
        this.client = client;
    }

    public ArrayList<ClientDataParentSer> getClient2() {
        return client2;
    }

    public void setClient2(ArrayList<ClientDataParentSer> client2) {
        this.client2 = client2;
    }
}

Any questions about anything comment below.

The full project is on my github here

Your expected JSON string is not normal because any JSON objects belong to the same JSON array should have the same structure, so the output JSON string should look like as below:

{
  "client":[
    {
      "member":12,
      "group":"g1"
    },
    {
      "member":17,
      "group":"g2"
    },
    {
      "member":29,
      "group":"all"
    }
  ],
  ...
}

If your expected JSON string can be revised so, then here comes another way to achieve what you want by following steps with Jackson and Lambda Expression (since Java 8):

Step 1
Create POJOs and use @JsonAnySetter to serialize client and client2 to List<ClientInfo> , and use @JsonIgnore for getName() for deserialization to ignore field name .

class RootPojo {
    private List<ClientInfo> clients = new ArrayList<>();

    @JsonAnySetter
    public void setClients(String name, List<ClientInfo> client) {
        client.forEach(e -> {
            e.setName(name);
        });
        this.clients.addAll(client);
    }

    //general getter and toString
}

class ClientInfo {
    private String name;
    private int member;
    private String group;

    @JsonIgnore
    public String getName() {
        return name;
    }

    //general getters, setters and toString
}

Step 2
Serialize JSON string to pre-defined POJOs with Jackson :

ObjectMapper mapper = new ObjectMapper();
RootPojo rootPojo = mapper.readValue(inputJsonStr, RootPojo.class);
System.out.println(rootPojo.toString());

Console output:

RootPojo [clients=[ClientInfo [name=client, member=12, group=g1], ClientInfo [name=client, member=17, group=g2], ClientInfo [name=client2, member=14, group=g11], ClientInfo [name=client2, member=175, group=g22]]]

Step 3
Use Lambda Expression for grouping and summation which will also add the results as new JSON objects back to original JSON string.

rootPojo.getClients()
    .stream()
    .collect(Collectors.groupingBy(ClientInfo::getName,
            Collectors.summingInt(ClientInfo::getMember)))
    .forEach((k,v) -> {
        ClientInfo clientInfo = new ClientInfo();
        clientInfo.setName(k);
        clientInfo.setGroup("all");
        clientInfo.setMember(v);
        rootPojo.getClients().add(clientInfo);
    });
System.out.println(rootPojo.toString());

Console output:

RootPojo [clients=[ClientInfo [name=client, member=12, group=g1], ClientInfo [name=client, member=17, group=g2], ClientInfo [name=client2, member=14, group=g11], ClientInfo [name=client2, member=175, group=g22], ClientInfo [name=client, member=29, group=all], ClientInfo [name=client2, member=189, group=all]]]

Step 4
Transform rootPojo into Map<String, List<ClientInfo> then deserialize it to output JSON string:

Map<String, List<ClientInfo>> clientMap = new HashMap<>();
rootPojo.getClients().forEach(e -> {
    if (clientMap.containsKey(e.getName())) {
        clientMap.get(e.getName()).add(e);
    } else {
        List<ClientInfo> clients = new ArrayList<>();
        clients.add(e);
        clientMap.put(e.getName(), clients);
    }
});

String outputJsonStr = mapper.writeValueAsString(clientMap);
System.out.println(outputJsonStr);

Console output:

{"client":[{"member":12,"group":"g1"},{"member":17,"group":"g2"},{"member":29,"group":"all"}],"client2":[{"member":14,"group":"g11"},{"member":175,"group":"g22"},{"member":189,"group":"all"}]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM