Publié le 03/09/2022
Auteur fobec
Réseaux sociaux
0 partages
0 tweets
0 plus
0 commentaires

Indenter et mettre en couleur un JSON

Dans le projet en cours, la configuration de l'application repose sur des fichiers JSON que l'utilisateur peut éditer. Pour faciliter la lecture et la modification du JSON, les éditeurs proposent une fonction nommée Syntax Highlighting. Ce terme anglais désigne la mise en couleur du code source précédée bien souvent par le formatage des données.

Présentation du format JSON

Pour rappel, un fichier JSON est un format d'échange de données au même titre que le format XML par exemple. Le but est de transmettre des valeurs d'une application à une autre. Le format JSON s'est imposé comme standard car sa structure est indépendante du langage de programmation et l'encodage en UTF-8 rend possible l'échange de tout type d'information.

Dans sa forme native, le format JSON se présente sous forme d'un texte réduit à son minimum. En effet, rien ne sert de s'encombrer d'espaces et d’éléments de style pour échanger des données entre deux serveurs.
Pour cet article, prenons ma liste des courses au format JSON. Dans sa plus simple représentation, les données se présentent sous cette forme:

{"Supermarchu00e9":{"viande":"poulet","u00e9picerie":["pates","riz"],"liquide":["eau minu00e9rale","vin","jus de fruit"]},"Primeur":{"fruit":"pommes","lu00e9gume":["carotte","poireaux","haricot"]},"Boulangerie":"pain","Poissonnerie":["crevettes","saumon","homard"],"Maison de la presse":{"papeterie":["stylo","cahier","pochette"],"presse":"magazine"}}

Effectivement, cette représentation des valeurs ne facilitent ni la lecture, ni l'édition des données. Pour rendre le format plus lisible pour un humain, il serait judicieux d'ajouter des retours à la ligne, des espaces et un peu de couleurs.

Fonctions de la class JsonHighlighter

L'idée est de permettre à l'utilisateur de l'application d'éditer un fichier JSON. Pour ce faire, les données sont affichées soit dans un TextArea soit dans un conteneur DIV.

Indentation du texte

Pour structurer les données, la première étape consiste à indenter le fichier d'origine. Le format JSON suit un certain nombre de règle qui peuvent être mises en évidence. En effet, les données essentielles sont plus faciles à lire avec des retours à la ligne et des décalages au niveau de la marge.

Mise en couleur

Un texte uniquement dans la couleur noir est difficile à lire. Cette manière de présenter l'information donne la même importance à chaque mot du texte. Le format JSON repose sur une collection de couples nom/valeur. Dans un premier temps, la couleur met en évidence s'il s'agit d'un clé ou d'une valeur. Dans un seconde temps, le changement de style identifie le type de valeur (string, integer, boolean).

Code source de la classe JsonHighlighter

<?php
 
namespace VendorString;
 
/**
 * JsonHighlighter
 * Indenter et mettre en couleur un Json
 * http://www.fobec.com/php5/1187/indenter-mettre-couleur-json.html
 * @author Fobec 04/09/2022
 */
class JsonHighlighter {
 
    /**
     * Préférences pour l'affichage texte
     */
    const RULES_INDENT_TEXT = [
        'padding' => ' ',
        'line_break' => "\n"
    ];
    
    /**
     * Préférences pour l'affichage HTML
     */
    const RULES_INDENT_HTML = [
        'padding' => '<span style="width:10px;height:5px;display:inline-block"></span>',
        'line_break' => "\n",
        'css_key_string' => 'color:#4e8475;font-weight:bold',
        'css_subkey_string' => 'color:#693',
        'css_value_string' => "color:#c33",
        'css_value_bool' => 'color:#2c5c97',
        'css_value_num' => 'color:#502d87'
    ];
 
    /**
     * Indenter pour un affichage texte
     * @param string $json
     * @return string
     */
    public function render_text(string $json): string {
        $out = $this->indent($json, JsonHighlighter::RULES_INDENT_TEXT);
        return $out;
    }
 
    /**
     * Indenter et appliquer des couleurs pour un affichage HTML
     * @param string $json
     * @return string
     */
    public function render_html(string $json): string {
        $out = $this->indent($json, JsonHighlighter::RULES_INDENT_HTML);
 
        $out_hightlight = $this->highlight($out, JsonHighlighter::RULES_INDENT_HTML);
 
        $out = str_replace("\n", '<br>', $out_hightlight);
        return $out;
    }
 
    /**
     * Indenter les lignes
     * @param string $json
     * @param array $RULES
     * @return string|array
     */
    private function indent(string $json, array $RULES) {
        //Ligne json en cours
        $inline = false;
        //Niveau d'indentation
        $level = 0;
        //Caractère à insérer avant
        $before_char = '';
        //Caractère à insérer après
        $after_char = '';
        //Sortie
        $result = '';
 
        //Contenu vide
        if (strlen($json) == 0) {
            return $json;
        }
 
 
        for ($i = 0; $i < strlen($json); $i++) {
            //Caractère précédent
            $prev_char = ($i > 0) ? $json[$i - 1] : '';
            //Caractère en cours
            $char = $json[$i];
            //à insérer avant
            $before_char = '';
            //à insérer après
            $after_char = '';
 
            //Saut à la ligne après {
            if (in_array($char, ['{'])) {
                $level++;
                $after_char = $RULES['line_break'];
            } else if (in_array($char, ['{', '[']) && $prev_char == ':') {
                $inline = true;
            } else if (in_array($char, ['}', ']']) && $inline == true) {
                $inline = false;
            } else if (in_array($char, ['}'])) {
                $before_char .= $RULES['line_break'];
                $level--;
            }
 
            if (in_array($char, [',']) && $inline == false) {
                $after_char = $RULES['line_break'];
            }
 
            $result .= $before_char;
            //Padding
            $last_result_char = strlen($result) > 0 ? $result[strlen($result) - 1] : '';
            if ($level > 0 && $last_result_char == $RULES['line_break']) {
                for ($loop_level = 0;
                        $loop_level < $level;
                        $loop_level++) {
                    $result .= $RULES['padding'];
                }
            }
 
            $result .= $char;
            $result .= $after_char;
        }
 
        return $result;
    }
 
    /**
     * Mettre en couleur les clés/valeurs du fichier Json
     * @param string $json_indent
     * @param array $RULES
     * @return type
     */
    private function highlight(string $json_indent, array $RULES) {
        $lines = explode("\n", $json_indent);
        $out = [];
 
        foreach ($lines as $line) {
            $part_indent = '';
            $part_key = '';
            $part_value = '';
            $is_subkey = false;
 
            //Indentation
            $pos_indent = strrpos($line, '</span>');
            if (is_numeric($pos_indent)) {
                $is_subkey = strpos($line, '</span>') != $pos_indent;
                $part_indent = substr($line, 0, $pos_indent + strlen('</span>'));
                $line = substr($line, $pos_indent + strlen('</span>'));
            }
            //couple valeur/clé
            $pos_dubble = strpos($line, ':');
            if (is_numeric($pos_dubble) && strpos($line, '::') !== $pos_dubble) {
                $part_key = substr($line, 0, $pos_dubble);
                $part_value = substr($line, $pos_dubble + 1);
                $new_key = $this->render_key($part_key, $RULES, $is_subkey);
                $new_value = $this->render_value($part_value, $RULES);
 
                $out[] = $part_indent . $new_key . ':' . $new_value;
            } else {
                $out[] = $part_indent . $line;
            }
        }
 
        return implode("n", $out);
    }
 
    /**
     * Apliquer un style aux clés
     * @param string $value
     * @param array $RULES
     * @param bool $is_subkey clé à partir du niveau 2
     * @return string
     */
    private function render_key(string $value, array $RULES, bool $is_subkey): string {
        // if (preg_match_all("/"[w-_:]*"/u", $value, $matches)) {
        $list_string_offsets = $this->get_string_offsets($value);
        
        if (count($list_string_offsets) > 0) {
            $buf = '';
            $buf .= substr($value, 0, $list_string_offsets[0]['start']);
            $sub = substr($value, $list_string_offsets[0]['start'], $list_string_offsets[0]['end'] - $list_string_offsets[0]['start'] + 1);
            if ($is_subkey == false) {
                $buf .= '<span style="' . $RULES['css_key_string'] . '">' . $sub . '</span>';
            } else {
                $buf .= '<span style="' . $RULES['css_subkey_string'] . '">' . $sub . '</span>';
            }
            $buf .= substr($value, $list_string_offsets[0]['end'] + 1);
            
            return $buf;
        }
        
        return $value;
    }
 
    /**
     * Appliquer un style en fonction du type de valeur
     * @param string $value
     * @param array $RULES
     * @return string
     */
    private function render_value(string $value, array $RULES): string {
        $list_string_offsets = $this->get_string_offsets($value);
 
        //String
        if (count($list_string_offsets) > 0) {
            $icur = 0;
            $buf = '';
            foreach ($list_string_offsets as $offset) {
                $buf .= substr($value, $icur, $offset['start'] - $icur);
                $sub = substr($value, $offset['start'], $offset['end'] - $offset['start'] + 1);
                $buf .= '<span style="' . $RULES['css_value_string'] . '">' . $sub . '</span>';
                $icur = $offset['end'] + 1;
            }
            $buf .= substr($value, $icur);
            return $buf;
        }
        //Boolean
        if (strpos($value, 'true') === 0 || strpos($value, 'false') === 0) {
            return '<span style="' . $RULES['css_value_bool'] . '">' . $value . '</span>';
        }
        //Number
        if (preg_match_all("/[d.]*/is", $value, $matches)) {
            foreach ($matches[0] as $key) {
                $replace = '<span style="' . $RULES['css_value_num'] . '">' . $key . '</span>';
                return str_replace($key, $replace, $value);
            }
        }
 
        return $value;
    }
 
    /**
     * Déterminer la position des string dans value
     * @param string $value
     * @return array ['start','end']
     */
    private function get_string_offsets(string $value): array {
        $cur_offset = 0;
        $out = [];
 
        $pos_quote_start = strpos($value, '"', $cur_offset);
        while (is_numeric($pos_quote_start)) {
            $pos_quote_end = strpos($value, '"', $pos_quote_start + 1);
            if (is_numeric($pos_quote_end)) {
                $out[] = ['start' => $pos_quote_start, 'end' => $pos_quote_end];
                $cur_offset = $pos_quote_end + 1;
            }
            $pos_quote_start = strpos($value, '"', $cur_offset);
        }
 
        return $out;
    }
 
}

Résultat


La JsonHighlighter est prévue pour éditer un fichier de configuration. L'utilisateur édite le fichier JSON dans un visionneur de type DIV. L'indentation et la mise en couleur facile la compréhension des valeurs.
Ci-dessous un exemple d'utilisation

$jsonHighlighter = new JsonHighlighter();
$html=$jsonHighlighter->render_html($sjon);
echo '<div contenteditable="true" spellcheck="false">';
echo $html;
echo '</div>';

Dans un premier temps, la classe JsonHighlighter met en forme un fichier JSON issu de la sauvegarde. L'utilisateur modifie les valeurs du fichier de configuration dans une DIV de type contenteditable=true. Puis les données au format JSON sont à nouveau sauvegarder sur le serveur.

Ajouter un commentaire

Les champs marqués d'un * sont obligatoires, les adresses emails se sont pas publiées.

A lire aussi

Réseaux sociaux
Présentation de l'article
Catégorie
php5 - class
Mise a jour
03/09/2022
Visualisation
vu 43 fois
Public
Internaute
Auteur de la publication
Fobec
Admin
Auteur de 267 articles
|BIO_PSEUDO|
Commentaires récents

Publié par nicolasterraes dans tuto

bonjour,
je ne comprends pas cette tape:

Les liens vers les feuilles de style et les fichier js sont declares dans les pages HTML. Modifiez les balises meta ainsi:
Feuille de style:
d...

Publié par Quentin dans tuto

Merci pour ces petits codes java script qui me sont d'un grand secours pour mon TP :)

Publié par samir dans CMS

merci

Publié par bob le poisson dans java

Bonjour, votre script est tres bien mais attention l'utilisation du mot cle static sur la methode scale(BufferedImage bImage, double factor) est inutile et empeche de liberer la ressource.

Publié par Fobec dans php5

Le code PHP sur les boucles a ete corrige dans l'article.
Merci Etiazam !