Json deserialization c# avec non conforme json

Résolu/Fermé
ryko1820 Messages postés 1645 Date d'inscription dimanche 28 avril 2013 Statut Membre Dernière intervention 15 août 2021 - Modifié par ryko1820 le 13/03/2016 à 16:27
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 - 14 mars 2016 à 17:59
Bonjour,

Je commence tout juste C# et j'essaye de-sérialiser en C# du JSON passé par un streamreader mais ce JSON, sur lequel je n'ai pas de moyen d'agir n'est pas conforme.

J'utilise Newtonsoft.Json pour la désérialisation ...

class Program
    {
        static void Main(string[] args)
        {
        using (StreamReader configFile = File.OpenText(@".\\config.json"))
        {
            JsonSerializer serializer = new JsonSerializer();
            MyConfig cfg = (MyConfig)serializer.Deserialize(configFile, typeof(MyConfig));
            Console.WriteLine("titi : " + cfg.titi);
        }
    }

public class MyConfig
    {
        public string titi { get; set; }
        public string toto { get; set; }
        public string tutu { get; set; }
    }
}

J'accède ensuite aux valeurs par cfg.titi, cfg.tutu, etc ...
Sauf que voici la tête de mon JSON (si je vire le premier objet manuellement j'ai mes valeurs) :

{
    "foo" : "1",
    "bar" : "1"
}{
     "titi" : "val1",
     "toto" : "val2",
     "tutu" : "val3"
}

Cela ne cause pas aucune erreur de compilation ou d’exécution mais je n'ai pas non plus mes valeurs.
Quand je passe mon fichier à travers d'un parseur comme jq, j'obtient pour, par exemple titi ...

jq ". | .titi" config.json

null
val1


J'ai essayé d'imposer un JsonSerializerSettings en MissingMemberHandling, NullValueHandling, et d'autres mais ça ne change rien ...

JsonConvert.SerializeObject(serializer,
        Formatting.None,
        new JsonSerializerSettings { 
            NullValueHandling = NullValueHandling.Ignore 
});


Peut-être devrais je virer mes 3 premières lignes directement dans le streamer ? (ce que pour l'instant je ne sais pas faire).
N'y aurait-il pas une façon de dire au parseur d'ignorer le premier objet ?




You may stop me but you can't stop us all   ;-)

4 réponses

Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
13 mars 2016 à 16:31
Bonjour,

le json je ne connais pas (enfin si, mais je n'ai jamais travaillé dessus), par contre pour sortir 3 lignes, tu peux passer par un fichier temporaire:

File.WriteAllLines("temp.json", File.ReadLines(@".\\config.json").Skip(3));


Sinon, peut être peux tu envisager d'écrire ton propre parseur.
0
ryko1820 Messages postés 1645 Date d'inscription dimanche 28 avril 2013 Statut Membre Dernière intervention 15 août 2021 276
Modifié par ryko1820 le 13/03/2016 à 16:47
Merci beaucoup pour tes suggestions, j'aimerais autant que possible éviter d'utiliser des fichiers temporaires ou alors en dernier recours.
Je vais essayer de comprendre la logique du parseur, et voir si il n'y aurait pas possibilité de gérer. Le souci c'est que je n'ai même pas d'erreur pour m'aider.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
13 mars 2016 à 18:04
Si j'ai bien compris dans
{
"foo" : "1",
"bar" : "1"
}{
"titi" : "val1",
"toto" : "val2",
"tutu" : "val3"
}


tu veux virer
{
"foo" : "1",
"bar" : "1"
}


Est ce que la séquence
{
"titi" : "val1",
"toto" : "val2",
"tutu" : "val3"
}


peut se répéter plusieurs fois?
0
ryko1820 Messages postés 1645 Date d'inscription dimanche 28 avril 2013 Statut Membre Dernière intervention 15 août 2021 276
Modifié par ryko1820 le 13/03/2016 à 19:13
En fait le plus simple pour que le JSON soit conforme, consisterait dans l'effacement de ...

{
    "foo" : "1",
    "bar" : "1"
}


... de mon stream parsé. Tout le reste passe les validateurs JSON quand cette espèce d' "entête" est effacée, et pourtant le fichier fait des centaines de lignes avec des tableaux, d'autres objets, etc ... mais complètement conforme : une paire clef/valeur n’apparaît qu'une fois, et la structure JSON est bien respectée.

Même le tout petit bout de JSON que j'ai saisi en haut ne franchit pas les validateurs stricts et a exactement la même structure que mon fichier réel.

Par contre le fichier passe sans problème et sans modification les différents parseurs que j'ai essayé, qui apparemment sont moins stricts sur la conformité mais aux dépends des résultats qui ne peuvent plus être obtenus, car ne pouvant être indexé (ou alors je ne vois pas comment).

Pour rendre valide, sans supprimer de données, il faudrait mettre les 2 objets dans un tableau d'objets de cette façon :

[
    {
        "foo": "1",
        "bar": "1"
    }, 
    {
        "titi": "val1",
        "toto": "val2",
        "tutu": "val3"
    }
]


Donc remplacer un
} {
par
},{
, le premier
{
du fichier par
[{
et le dernier
}
par
}]
avant de soumettre le stream au parseur. Le plus simple est certainement, effectivement, de modifier le fichier,de le dumper puis de le rouvrir. Je me demandais si il n'y avait pas possibilité de modifier les données "au vol" :p ou si il n'est pas possible de dire au parseur d'ignorer une partie des données.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
Modifié par Whismeril le 13/03/2016 à 19:36
Ha zut,

j'avais travaillé sur l'hypothèse que tu n'avais que des "Config", pour ta info ça donnait ça:
  • le fichier de test

{
"foo" : "1",
"bar" : "1"
}{
"titi" : "val1",
"toto" : "val2",
"tutu" : "val3"
}
{
"titi" : "val4",
"toto" : "val5",
"tutu" : "val6"
}{
"foo" : "1",
"bar" : "1"
}{
"titi" : "val7",
"toto" : "val8",
"tutu" : "val9"
}
  • la classe

    public class MyConfig
    {
        public string titi { get; set; }
        public string toto { get; set; }
        public string tutu { get; set; }

        public static List<MyConfig> Deserialize(string Filename)
        {
            List<MyConfig> mesConfigs = new List<MyConfig>();


            Regex maRegex = new Regex("({\\r\\n     "titi" : "(?<Titi>\\w+)",\\r\\n     "toto" : "(?<Toto>\\w+)",\\r\\n     "tutu" : "(?<Tutu>\\w+)"\\r\\n})+");

            foreach(Match m in maRegex.Matches(File.ReadAllText(Filename)))
               {
                   mesConfigs.Add(new MyConfig
                       {
                           titi = m.Groups["Titi"].Value,
                           toto = m.Groups["Toto"].Value,
                           tutu = m.Groups["Tutu"].Value
                       });
               }

            return mesConfigs;
        }
    }
  • la récupération de la liste de MyConfig

List<MyConfig> configs = MyConfig.Deserialize("test.json");

Quand j'étais petit, la mer Morte n'était que malade.
George Burns
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
Modifié par Whismeril le 13/03/2016 à 19:45
Je pense que la solution est là
https://codes-sources.commentcamarche.net/source/24620-string-to-stream

Et ce string en entrée:
            string TexteSansMoins3Lignes = string.Join("\r\n", File.ReadAllLines("test.json").Skip(3));

Si tu as bien une ligne qui commence par }{, il faudra aussi virer }
Quand j'étais petit, la mer Morte n'était que malade.
George Burns
0
ryko1820 Messages postés 1645 Date d'inscription dimanche 28 avril 2013 Statut Membre Dernière intervention 15 août 2021 276
Modifié par ryko1820 le 13/03/2016 à 21:26
Waou !!

Merci !!!, je vais regarder tout ça et voir ce que j'arrive à en faire.

Pour ceux que ça intéresse, l'adresse d'un validateur JSON : https://jsonlint.com/
0
ryko1820 Messages postés 1645 Date d'inscription dimanche 28 avril 2013 Statut Membre Dernière intervention 15 août 2021 276
Modifié par ryko1820 le 14/03/2016 à 17:20
Hello,

avec ton aide (et celle de Visual studio :p) voilà ce que j'ai fait (ça tourne nickel !) :

En fait, je n'ai pas eu besoin de convertir la string en stream car c'est un textReader qu'attends le serializer (et que renvoyait StreamReader ici), ce qui donne :


using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                Environment.CurrentDirectory = Environment.GetEnvironmentVariable("appdata") + "\\myProg";

                // Mise en conformité JSON par retrait de l'objet inutile et mal formaté
                string coreConfNormalized = string.Join("\r\n", File.ReadAllLines(".\\config.json").Skip(3)).Replace("}{", "{");

                // using (StreamReader configFile = File.OpenText(@".\\config.json"))
                // Création TextReader à partir de la String coreConfNormalized (fichier JSON corrigé)
                using (var configFile = new StringReader(coreConfNormalized))
                {
                    JsonSerializer serializer = new JsonSerializer();
                    MyConfig cfg = (MyConfig)serializer.Deserialize(configFile, typeof(MyConfig));
                    Console.WriteLine("titi : " + cfg.titi);

                    Console.ReadLine();
                }
            }
        }

public class MyConfig
        {
            public string titi { get; set; }
            public string toto { get; set; }
            public string tutu { get; set; }
        }
    }
}


... je lis pas mal de JSON dans ce prog que j'écris, (j'ai un peu tendance aussi bien sous Linux que dans Windows ou sur le web (en Webservices) à en mettre partout, fichier de config, historisation, IO BDD, etc ...) mais les autres je les écris moi-même ce qui me permet de m'assurer de leur validité ...
Voilà donc une combine qui permets de corriger un JSON au vol :-)
(et accessoirement de convertir une string en textReader) ...

Encore merci Whismeril

J'ai corrigé l'exemple pour qu'il soit fonctionnel tel-quel pour peu qu'un fichier "config.json" (au format "foireux" décrit plus haut) existe dans "%addpdata%\myApp"
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
14 mars 2016 à 17:59
De rien.

Je fais de même avec les xml!
0