Parsing Java [Résolu]

Signaler
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020
-
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020
-
Bonjour à tous,

J'aimerai parser mon JSON :

{
"STATUS": "OK",
"todo-items": [
{

"content": "Reporting Teamwork: récupérer travaux API (30%)",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"subTasks": [
{
"content": "HTTP request",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"parent-task": {
"content": "Reporting Teamwork: récupérer travaux API (30%)",
},
},
{
"content": "Proxy configuration",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"parent-task": {
"content": "Reporting Teamwork: récupérer travaux API (30%)",
},
}
],
},
{
"content": "Reporting Teamwork: récupérer travaux API (60%)",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"subTasks": [
{
"content": "Authentication API",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"parent-task": {
"content": "Reporting Teamwork: récupérer travaux API (60%)",
},
},
{
"content": "Parsing",
"project-name": "Governance and Follow-up SII",
"todo-list-name": "Teamwork",
"parent-task": {
"content": "Reporting Teamwork: récupérer travaux API (60%)",
},
}
],
}
]
}


J'ai un code qui le parse :


public void Parsing(String jsonToParse) throws Exception{
  
  JSONParser parse = new JSONParser();
  JSONObject jobj = (JSONObject)parse.parse(jsonToParse);
  JSONArray jsonarr_1 = (JSONArray) jobj.get("todo-items");
  
  for(int i=0;i<jsonarr_1.size();i++)
  {
 
   JSONObject jsonobj_1 = (JSONObject)jsonarr_1.get(i);
   System.out.println("\n Project : " +jsonobj_1.get("project-name"));
   System.out.println("Liste : " +jsonobj_1.get("todo-list-name"));
   System.out.println("Content : " +jsonobj_1.get("content"));

    
    //Store the JSON object in JSON array as objects (For level 2 array element i.e Address Components)
   JSONArray jsonarr_2 = (JSONArray) jsonobj_1.get("subTasks");
  
    
    for(int j=0;j<jsonarr_2.size();j++)
    {
     JSONObject jsonobj_2 = (JSONObject) jsonarr_2.get(j);
     String str_data1 = (String) jsonobj_2.get("content");
    System.out.println(" sous tâches : " + str_data1);
   
    }
    }
 
  
 }




Et me renvoie ça :

Project : Governance and Follow-up SII
Liste : Teamwork
Content : Reporting Teamwork: récupérer travaux API (30%)
sous tâches : HTTP request
sous tâches : Proxy configuration


Project : Governance and Follow-up SII
Liste : Teamwork
Content : Reporting Teamwork: récupérer travaux API (60%)
sous tâches : Authentication API
sous tâches : Parsing

J'affiche ça via une boucle
Mais je voudrais récuperer ça sous cette forme :

Project : Governance and Follow-up SII
Liste : Teamwork
Content : Reporting Teamwork: récupérer travaux API (30%)
sous tâches : HTTP request
sous tâches : Proxy configuration
Content : Reporting Teamwork: récupérer travaux API (60%)
sous tâches : Authentication API
sous tâches : Parsing

PS: mon json passe par cette méthode avant dêtre traité :



public String streamToString(InputStream in) throws IOException {
		StringBuilder out = new StringBuilder();
		BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));
		for(String line = br.readLine(); line != null; line = br.readLine()) 
		out.append(line);
		br.close();
		return out.toString();
	}





Si quelqu'u à une idée merci !

4 réponses

Messages postés
3641
Date d'inscription
jeudi 16 juin 2005
Statut
Membre
Dernière intervention
21 octobre 2020
972
Bonjour,

Deux cas.
Soit ton JSON est déjà trié par Project et par List.
C'est le cas simple : il te suffit de stocker la valeur précédente de ces champs, et de ne faire println que s'ils ont changé.

Soit ton JSON n'est pas trié, auquel cas tu ne dois rien afficher au fur et à mesure du parsing, mais tout stocker dans une structure adaptée (je ne maîtrise pas assez les collections et listes en Java pour te dire quelle est la meilleure) pour, ensuite, trier et parcourir tout ça pour les afficher.

Xavier
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Bonjour,

Merci pour ta réponse,

Qu'est ce que tu entends par "Soit ton JSON est déjà trié par Project et par List" ?

Gloria
Messages postés
3641
Date d'inscription
jeudi 16 juin 2005
Statut
Membre
Dernière intervention
21 octobre 2020
972 >
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

La solution que je préconise, c'est de n'écrire le Project et le List que s'ils ont changé.
Mais ça ne peut marcher que si ton JSON est de la forme :

{
ProjectA
ListA
blabla1
}
{
ProjectA
ListA
blabla2
}
{
ProjectB
ListB
blabla3
}

Et non pas

{
ProjectA
ListA
blabla1
}
{
ProjectB
ListB
blabla3
}
{
ProjectA
ListA
blabla2
}

Car dans ce cas, on change de Project et de List avant d'avoir écrit toutes les sous-rubriques de ces projets/listes.
Donc il faut que le JSON soit déjà ordonné par ces champs-là.

Xavier
Messages postés
16065
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 octobre 2020
2 699
Bonjour,

Tout d'abord quand tu utilises des librairies tierces, il est important de préciser lesquelles, ou au moins les imports correspondants, parce que dans ton code si j'arrive à deviner que JSONObject et JSONArray viennent de org.json, on peut quand même se poser des questions sur JSONParser (d'où vient-il ?) ou sur la méthode size() de JSONArray qui n'existe pas (c'est length), ce qui laisse à penser que ton code ne compile pas, ou que tu utilises d'autres librairies.

De plus, Java est un langage objet, pour manipuler tes données, tu dois créer des classes, notamment ici pour représenter les données du Json, dans des DTO (Data Transfert Object).

Exemple :
import java.util.List;

public class ItemDto {
    public String content;
    public String projectName;
    public String todoListName;
    public List<TaskDto> subTasks;

    public String toString(){
        return "ItemDto [content=" + content + ", projectName=" + projectName + ", todoListName=" + todoListName + ", subTasks=" + subTasks + "]";
    }
}
public class TaskDto {
    public String content;
    public String projectName;
    public String todoListName;

    public String toString(){
        return "TaskDto [content=" + content + ", projectName=" + projectName + ", todoListName=" + todoListName + "]";
    }
}

Ton Parser peut alors se réécrire comme ceci :
import java.util.*;
import org.json.*;

public class Parser {

    public static List<ItemDto> readJson(String json) {
        JSONObject rootJson = new JSONObject(json);
        JSONArray itemsJson = (JSONArray) rootJson.get("todo-items");
        List<ItemDto> items = new ArrayList<>(itemsJson.length());
        for (int i = 0; i < itemsJson.length(); i++) {
            items.add(readItem((JSONObject) itemsJson.get(i)));
        }
        return items;
    }

    public static ItemDto readItem(JSONObject itemJson) {
        ItemDto item = new ItemDto();
        item.content = itemJson.getString("content");
        item.projectName = itemJson.getString("project-name");
        item.todoListName = itemJson.getString("todo-list-name");

        JSONArray tasksJson = (JSONArray) itemJson.get("subTasks");
        item.subTasks = new ArrayList<>(tasksJson.length());
        for (int i = 0; i < tasksJson.length(); i++) {
            item.subTasks.add(readTask((JSONObject) tasksJson.get(i)));
        }

        return item;
    }

    public static TaskDto readTask(JSONObject taskJson) {
        TaskDto task = new TaskDto();
        task.content = taskJson.getString("content");
        task.projectName = taskJson.getString("project-name");
        task.todoListName = taskJson.getString("todo-list-name");
        return task;
    }
}

Une petite classe de test, pour vérifier que ça fonctionne bien :
import java.nio.file.*;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception {
        List<ItemDto> items = Parser.readJson(Files.readString(Paths.get("E:/test.json")));
        System.out.println(items);
    }
}

Ton problème, c'est que cette représentation correspond au Json, pas à ce que tu souhaites manipuler, il faut donc créer d'autres objets, qui correspondent à ce que tu veux en faire.
import java.util.List;

public class ProjectBean {
    public HeaderBean header;
    public List<ItemBean> items;

    public String toString(){
        return "ProjectBean [header=" + header + ", items=" + items + "]";
    }
}

import java.util.Objects;

public class HeaderBean {
    public String projectName;
    public String todoListName;

    public String toString(){
        return "HeaderBean [projectName=" + projectName + ", todoListName=" + todoListName + "]";
    }

    public int hashCode(){
        return Objects.hash(projectName, todoListName);
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        HeaderBean other = (HeaderBean) obj;
        return Objects.equals(projectName, other.projectName) && Objects.equals(todoListName, other.todoListName);
    }
}

import java.util.List;

public class ItemBean {
    public String content;
    public List<String> subTasksContent;

    public String toString(){
        return "ItemBean [content=" + content + ", subTasksContent=" + subTasksContent + "]";
    }
}

Il faut ensuite faire le mapping entre les DTO et les Bean, c'est là que tu vas devoir faire des regroupements, en l’occurrence sur le HeaderBean, grâce à une Map par exemple.

import java.util.*;

public class ProjectMapper {
    public static List<ProjectBean> projectsFromItems(List<ItemDto> itemDtos) {
        Map<HeaderBean, List<ItemBean>> map = new HashMap<>();
        for (ItemDto itemDto : itemDtos) {
            HeaderBean headerBean = new HeaderBean();
            headerBean.projectName = itemDto.projectName;
            headerBean.todoListName = itemDto.todoListName;

            ItemBean itemBean = new ItemBean();
            itemBean.content = itemDto.content;
            itemBean.subTasksContent = new ArrayList<>();
            for (TaskDto taskDto : itemDto.subTasks) {
                itemBean.subTasksContent.add(taskDto.content);
            }

            List<ItemBean> itemBeans = map.get(headerBean);
            if (itemBeans == null) {
                itemBeans = new ArrayList<>();
                map.put(headerBean, itemBeans);
            }
            itemBeans.add(itemBean);
        }
        List<ProjectBean> projects = new ArrayList<>();
        for (Map.Entry<HeaderBean, List<ItemBean>> entry : map.entrySet()) {
            ProjectBean project = new ProjectBean();
            project.header = entry.getKey();
            project.items = entry.getValue();
            projects.add(project);
        }
        return projects;
    }
}

Si on adapte la méthode main, on peut reproduire l'affichage que tu souhaitais :
public static void main(String[] args) throws Exception {
    List<ItemDto> items = Parser.readJson(Files.readString(Paths.get("E:/test.json")));
    List<ProjectBean> projects = ProjectMapper.projectsFromItems(items);
    for (ProjectBean project : projects) {
        System.out.println("Project : " + project.header.projectName);
        System.out.println("List : " + project.header.todoListName);
        for (ItemBean item : project.items) {
            System.out.println("Content : " + item.content);
            for (String subTask : item.subTasksContent) {
                System.out.println("sous tâches : " + subTask);
            }
        }
    }
}

Avec ce résultat, comme attendu :
Project : Governance and Follow-up SII
Liste : Teamwork
Content : Reporting Teamwork: récupérer travaux API (30%)
sous tâches : HTTP request
sous tâches : Proxy configuration
Content : Reporting Teamwork: récupérer travaux API (60%)
sous tâches : Authentication API
sous tâches : Parsing
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Bonjour KX,

Merci beaucoup, ton code fonctionne parfaitement ! Tu me sauves la vie
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Bonjur KX, j'aurai une autre question.
Je viens de tester mon api mais avec d'autres paramètres (en l’occurrence la date des tâches). Certaine tâches n'ont pas de sous tâches, et lorsque j'appelle des tâches sans sous tâches, mon application me renvoie l'erreur suivante :

GRAVE: "Servlet.service()" pour la servlet [jsp] a lancé une exception
org.json.JSONException: JSONObject["subTasks"] not found.

En effet, lorsque je regarde la reponse brute en JSON, le nom "subtasks" n'y est pas. Je pensais qu'elle y serai mais avec une valeur null mais même pas...

Tu aurais une idée ?

Merci
Messages postés
16065
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 octobre 2020
2 699 >
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

C'est le comportement normal des JSONObject, lorsque l'attribut demandé n'existe pas cela provoque une JSONException.

Dans ce cas tu peux faire un try/catch pour gérer un comportement alternatif lorsque l'attribut est manquant.
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Re Bonjour,

J'ai essayé mais ça fait 1 semaine que je bloque, tu aurais une idée de comment faire ?

Merci
Messages postés
16065
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
21 octobre 2020
2 699
Bonjour,

Je t'ai déjà expliqué comment faire, tu dois ajouter un try/catch et gérer un comportement alternatif lorsque l'attribut est manquant.

Par exemple :
try {
    JSONArray tasksJson = (JSONArray) itemJson.get("subTasks");
    item.subTasks = new ArrayList<>(tasksJson.length());
    ...
} catch (JSONException e) { // subTasks n'existe pas
    item.subTasks = new ArrayList<>();
}
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Bonjour KX,

Merci beaucoup, en effet j'avais laisser la partie dans le catch vide.
Messages postés
7
Date d'inscription
lundi 21 septembre 2020
Statut
Membre
Dernière intervention
14 octobre 2020

Re Bonjour KX,

J'aurai une autre question, j'aurai besoin de cette fois ci filtrer mes tâches, exemple : récupérer seulement les tâches avec "Teamwork" comme "todo-list-name", je ne trouve pas du tout comment faire...