Come abbellire parzialmente JSON.stringify ()?

Oct 01 2020

Gli utenti possono scaricare file JSON dalla nostra applicazione, che desidero abbellire per un debug più semplice rispetto a se tutto fosse in una riga. Tuttavia, questo aumenta la dimensione del file di quasi il 40% anche se il rientro è solo una singola scheda o spazio.

Come compromesso, voglio escludere tutti i valori con ad esempio la chiave "grande" dall'abbellimento, in questo modo:

{
 "small":
 {
  "a": 1,
  "b": 2,
  "large": {"a":1,"b":2,"c":3,"d":"the \"large\" object should not be prettified"} 
 },
  "large": {"a":1,"b":2,"c":3,"d":"the \"large\" object should not be prettified"}
}

Ho provato a risolverlo utilizzando il parametro di sostituzione di JSON.stringify:

JSON.stringify(data,(key,value)=>key==="large"?JSON.stringify(data):value,'\t');

Tuttavia i valori per le chiavi "grandi" finiscono con l'escape:

data = 
{
     "small":
     {
      "a": 1,
      "b": [2,3,4,5],
      "large": {"myarray":[1,2,3,4],"b":2,"c":3,"d":"the \"large\" object should not be prettified"} 
     },
      "large": {"a":1,"b":2,"c":3,"d":"the \"large\" object should not be prettified"}
    }
    
 const json =  JSON.stringify(data,(key,value)=>key==="large"?JSON.stringify(data):value,'\t');
 document.getElementById("json").innerText = json;
<html>
<body>
<pre id="json">
</pre>
</body>
</html>

Come posso evitare che si verifichi questa fuga o in altro modo abbellire parzialmente JSON in JavaScript?

Risposte

2 sbgib Oct 06 2020 at 16:00

Ogni volta che chiami JSON.stringifyun oggetto (o parte di un oggetto) più di una volta , ti ritroverai con segni di discorso sfuggiti. In questo caso, dopo che la JSON.stringifyfunzione interna è stata applicata all'interno della funzione di sostituzione, non c'è modo di dire alla JSON.stringifyfunzione esterna di non trattare quell'istanza come farebbe con qualsiasi altra stringa.

Quindi, penso che se il tuo obiettivo è quello di stringere "parzialmente" un oggetto, devi scrivere una funzione che lo implementa. Per questo problema, suggerirei qualcosa del genere:

data = {
  "small": {
    "a": 1,
    "b": [2, 3, 4, 5],
    "c": [2, {"test":"example"}, [4,4], 5],
    "large": {
      "myarray": [1, 2, 3, 4],
      "b": 2,
      "c": 3,
      "d": "the \"large\" object should not be prettified"
    }
  },
  "large": {
    "a": 1,
    "b": 2,
    "c": 3,
    "d": "the \"large\" object should not be prettified"
  }
}

function print(obj, exclude, space) {
  let recur = (obj, spacing, inarray) => {
    let txt = '';

    if (inarray) {
      if (Array.isArray(obj)) {        
        txt += '[';

        for(let i=0;i<obj.length;i++) {
          txt += recur(obj[i], spacing + space, true);
        };

        txt = txt.substr(0, Math.max(1,txt.length - 2)) + ']';
        
      } else if (typeof obj === 'object' && obj !== null) {
        txt += '{' + recur(obj, spacing + space, false) + '\n' + spacing + '}';
      } else if (typeof obj === 'string') {
        txt += obj.replaceAll(/\"/g, '\\"') + '"';
      } else {
        txt += obj;
      };
      
      return txt + ', ';
      
    } else {
      for (let key of Object.keys(obj)) {
        if (exclude.indexOf(key) !== -1) {
          txt += '\n' + spacing + '"' + key + '": ' + JSON.stringify(obj[key]);
        } else if (Array.isArray(obj[key])) {
          txt += '\n' + spacing + '"' + key + '": [';
          
          for(let i=0;i<obj[key].length;i++) {
            txt += recur(obj[key][i], spacing + space, true);
          };
          
          txt = txt.substr(0, Math.max(1,txt.length - 2)) + ']';
          
        } else if (typeof obj[key] === 'object' && obj[key] !== null) {
          txt += '\n' + spacing + '"' + key + '": {' + recur(obj[key], spacing + space, false) + '\n' + spacing + '}';
        } else if (typeof obj[key] === 'string') {
          txt += '\n' + spacing + '"' + key + '": "' + obj[key].replaceAll(/\"/g, '\\"') + '"';
        } else {
          txt += '\n' + spacing + '"' + key + '": ' + obj[key];
        };
        
        txt += ',';
      };
      
      return txt.substr(0, txt.length - 1);
    };

  };
  return (Array.isArray(obj) ? '[' + recur(obj, space, true) + '\n' + ']' : '{' + recur(obj, space, false) + '\n' + '}');
};

document.getElementById("json").innerText = print(data, ['large'], '\t');
<html>
<body>
  <pre id="json">
</pre>
</body>
</html>

Qui viene utilizzata una funzione ricorsiva, che scorre tra le chiavi di un oggetto e aggiunge la coppia chiave-valore a una stringa, gestendo diversi tipi di dati. Se keysi trova excludenell'array, allora è stringa.

Scorrendo le chiavi e applicando in modo condizionale JSON.stringifydove richiesto, si evita efficacemente il problema di cui sopra.

Sono sicuro che potrebbero esserci casi limite a cui prestare attenzione quando si scrive il JSON da soli invece di utilizzare una funzione integrata, ma è possibile adattare qualcosa del genere in base alle proprie esigenze specifiche.

EDIT: risposta aggiornata per considerare meglio gli array all'interno dell'oggetto.