web-dev-qa-db-fra.com

Rogner les espaces dans la clé d'objet et la valeur de manière récursive

Comment tailler les espaces dans les clés et les valeurs d'un objet JavaScript de manière récursive?

Je suis tombé sur un problème dans lequel j'essayais de "nettoyer" une chaîne JSON fournie par l'utilisateur et de l'envoyer dans mon autre code pour un traitement ultérieur.

Supposons que nous ayons une chaîne JSON fournie par l'utilisateur dont la clé de propriété et la valeur sont du type "chaîne". Cependant, ce qui est problématique dans ce cas, c'est que les clés et les valeurs ne sont pas aussi propres que souhaité. Dites un {"key_with_leading_n_trailing_spaces": "my_value_with_leading_spaces"}.

Dans ce cas, cela peut facilement poser problème avec votre programme JavaScript brillamment écrit qui tente d’utiliser ces données (ou devrions-nous appeler cela des données modifiées?) la clé ne correspond pas mais la valeur ne peut pas non plus être mise en correspondance. J'ai regardé autour de Google et trouvé quelques astuces, mais il n'y a pas de remède qui guérisse tout.

Étant donné ce JSON avec beaucoup d'espaces blancs dans les clés et les valeurs.

var badJson = {
  "  some-key   ": "    let it go    ",
  "  mypuppy     ": "    donrio   ",
  "   age  ": "   12.3",
  "  children      ": [
    { 
      "   color": " yellow",
      "name    ": "    alice"
    },    { 
      "   color": " silver        ",
      "name    ": "    bruce"
    },    { 
      "   color": " brown       ",
      "     name    ": "    francis"
    },    { 
      "   color": " red",
      "      name    ": "    york"
    },

  ],
  "     house": [
    {
      "   name": "    mylovelyhouse     ",
      " address      " : { "number" : 2343, "road    "  : "   boardway", "city      " : "   Lexiton   "}
    }
  ]

};

Alors voici ce que je suis venu avec (avec l'aide de l'utilisation de lodash.js):

//I made this function to "recursively" hunt down keys that may 
//contain leading and trailing white spaces
function trimKeys(targetObj) {

  _.forEach(targetObj, function(value, key) {

      if(_.isString(key)){
        var newKey = key.trim();
        if (newKey !== key) {
            targetObj[newKey] = value;
            delete targetObj[key];
        }

        if(_.isArray(targetObj[newKey]) || _.isObject(targetObj[newKey])){
            trimKeys(targetObj[newKey]);
        }
      }else{

        if(_.isArray(targetObj[key]) || _.isObject(targetObj[key])){
            trimKeys(targetObj[key]);
        }
      }
   });

}

//I stringify this is just to show it in a bad state
var badJson = JSON.stringify(badJson);

console.log(badJson);

//now it is partially fixed with value of string type trimed
badJson = JSON.parse(badJson,function(key,value){
    if(typeof value === 'string'){
        return value.trim();
    }
    return value;
});

trimKeys(badJson);

console.log(JSON.stringify(badJson));

Remarque ici: j'ai fait cela en 1, 2 étapes parce que je ne pouvais pas trouver un meilleur one shot pour traiter toute la solution. S'il y a un problème dans mon code ou quelque chose de mieux, veuillez le partager avec nous.

Merci!

14
vichsu

Vous pouvez nettoyer les noms de propriété et les attributs en utilisant Object.keys pour obtenir un tableau des clés, puis Array.prototype.reduce pour parcourir les clés et créer un nouvel objet avec des clés et des valeurs coupées. . La fonction doit être récursive afin de supprimer également les objets et les tableaux imbriqués. 

Notez qu’il ne s’agit que de tableaux et d’objets simples. Si vous souhaitez traiter d’autres types d’objets, l’appel à reduction doit être plus sophistiqué pour déterminer le type d’objet (par exemple, une version intelligemment appropriée de nouvel obj.constructor () ).

function trimObj(obj) {
  if (!Array.isArray(obj) && typeof obj != 'object') return obj;
  return Object.keys(obj).reduce(function(acc, key) {
    acc[key.trim()] = typeof obj[key] == 'string'? obj[key].trim() : trimObj(obj[key]);
    return acc;
  }, Array.isArray(obj)? []:{});
}
18
RobG

Vous pouvez simplement le stringifier, le remplacer par une chaîne et le réparer.

JSON.parse(JSON.stringify(badJson).replace(/"\s+|\s+"/g,'"'))
30
epascarello

la réponse d'epascarello ci-dessus plus quelques tests unitaires (juste pour que je sois sûr):

function trimAllFieldsInObjectAndChildren(o: any) {
  return JSON.parse(JSON.stringify(o).replace(/"\s+|\s+"/g, '"'));
}

import * as _ from 'lodash';
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren(' bob '), 'bob'));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren('2 '), '2'));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren(['2 ', ' bob ']), ['2', 'bob']));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren({'b ': ' bob '}), {'b': 'bob'}));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren({'b ': ' bob ', 'c': 5, d: true }), {'b': 'bob', 'c': 5, d: true}));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren({'b ': ' bob ', 'c': {' d': 'alica c c '}}), {'b': 'bob', 'c': {'d': 'alica c c'}}));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren({'a ': ' bob ', 'b': {'c ': {'d': 'e '}}}), {'a': 'bob', 'b': {'c': {'d': 'e'}}}));
assert.true(_.isEqual(trimAllFieldsInObjectAndChildren({'a ': ' bob ', 'b': [{'c ': {'d': 'e '}}, {' f ': ' g ' }]}), {'a': 'bob', 'b': [{'c': {'d': 'e'}}, {'f': 'g' }]}));
3
Richard

Similaire à la réponse d'epascarello. C'est ce que j'ai fait : 

import Java.util.regex.Matcher;
import Java.util.regex.Pattern;

........

public String trimWhiteSpaceAroundBoundary(String inputJson) {
    String result;
    final String regex = "\"\\s+|\\s+\"";
    final Pattern pattern = Pattern.compile(regex);
    final Matcher matcher = pattern.matcher(inputJson.trim());
    // replacing the pattern twice to cover the Edge case of extra white space around ','
    result = pattern.matcher(matcher.replaceAll("\"")).replaceAll("\"");
    return result;
}

Cas de test 

assertEquals("\"2\"", trimWhiteSpace("\" 2 \""));
assertEquals("2", trimWhiteSpace(" 2 "));
assertEquals("{   }", trimWhiteSpace("   {   }   "));
assertEquals("\"bob\"", trimWhiteSpace("\" bob \""));
assertEquals("[\"2\",\"bob\"]", trimWhiteSpace("[\"  2  \",  \"  bob  \"]"));
assertEquals("{\"b\":\"bob\",\"c c\": 5,\"d\": true }",
              trimWhiteSpace("{\"b \": \" bob \", \"c c\": 5, \"d\": true }"));
0
some random guy