Как преобразовать текст, подобный XML, в объект javascript

#javascript #xml #json #parsing #text-parsing

#javascript #xml #json #Синтаксический анализ #синтаксический анализ текста

Вопрос:

Я создал функцию для этого.

 var text="adsf [name]Victor[/name] dummytext [name]Elliot[/name] asdf [name]Jake[/name] asdf [foo]bar[/foo]";

alert( readTags(text,'name') ); //Victor,Elliot,Jake
alert( readTags(text,'foo') ); //bar
  

но теперь мне нравится реализовывать функцию, которая получает строку, подобную этой

 [person]
    [name]jake[/name]
    [age]12[/age]
[/person]
  

и вернуть объект, подобный этому

 var object={};
object['person']={};
object['name']='jake';
object['age']='12';
return(object);
  

но я не знаю, как выполнить цикл по тексту. Как работать с начальными и конечными тегами?
Нравится

 [tag] [tag]value[/tag] [/tag]
  

Я думал найти начальный тег слева и конечный тег справа, используя indexOf('[tag]') и lastindexOf('[/tag]')

но в этой ситуации не работает

 [tag]value[/tag] [tag]value[/tag]
  

это предыдущая функция

 function readTags(str,property){

    var beginTag='[' property ']';
    var endTag='[/' property ']';

    var values=new Array(0);

    while(str.indexOf(beginTag)!=-1){
        values[values.length]=strBetween(str,beginTag,endTag);
        str=str.substring(str.indexOf(endTag) endTag.length);
    }
    return(values);
}

function strBetween(string,strBegin,strEnd){    //StrBetween("abcdef","b","e")  //return "cd"

    var posBegin, posEnd;

    posBegin=string.indexOf(strBegin);
    string=string.substring(posBegin   strBegin.length);
    posEnd=string.indexOf(strEnd);
    string=string.substring(0,posEnd);

    if ((posBegin==-1)||(posEnd==-1)){
        return(null);
    }else{
        return(string);
    }
}
  

Комментарии:

1. Не делайте этого. Вместо этого используйте JSON .

2. @PointedEars Я использую блоги для размещения контента, который может быть захвачен подобными роботами, почти все они добавляют мусор в JSON или не допускают теги XML или <>

Ответ №1:

Если у вас нет веской причины не использовать JSON, не делайте этого. JSON очень хорошо справляется со всеми этими проблемами и может довольно легко перемещаться с сервера на клиент и наоборот.

Но поскольку это кажется забавным, я попробую и посмотрю, смогу ли я придумать ответ.


Поскольку ваша структура напоминает XML, просто замените скобки на < и > и проанализируйте его как XML:

 text = text.replace('[', '<').replace(']', '>');

if (typeof DOMParser != "undefined") {
  var parser = new DOMParser();
  var xml = parser.parseFromString(text, 'text/xml');
} else {
  var xml = new ActiveXObject('Microsoft.XMLDOM');
  xml.async = 'false';
  xml.loadXML(text); 
}
  

Теперь xml содержит DOMDocument , который вы можете разобрать:

 xml.getElementsByTagName('person').childnodes;
  

Попробуйте этот, возможно, работающий код (не тестировался):

 function createObject(element) {
  var object = {};

  if (element.childNodes.length > 0) {
    for (child in element.childnodes) {
      object[element.tagName] = createObject(child);
    }

    return object;
  } else {
    return element.nodeValue;
  }
}
  

Комментарии:

1. умное решение! это кроссбраузерный? теперь я думаю, что могу использовать getElementsByTagName("*") для зацикливания всех элементов и генерации объекта. Я прав?

2. Вторая часть if обрабатывает IE, который является проблемой в каждом случае. Что касается второй части, конечно. Я бы лично придерживался childnodes и tagName для итерации. Это упростит вашу задачу.

3. (на самом деле, это была моя следующая идея) Не используйте window здесь; используйте this , если необходимо, но лучше if (typeof DOMParser != "undefined") . Объявляйте text , parser xml и т.д. Переменные с помощью var . Возвращает NodeList , так должно быть getElementsByTagName("person")[0] . Должно быть childNodes (чувствительно к регистру). DOMParser на данный момент довольно кросбраузерен. Для IE / MSHTML у вас есть вторая ветвь; однако требуется включенная поддержка ActiveX. Передача «*» вернет список узлов всех элементов. Но он будет плоским, поэтому вы не сможете создать из него свой объект. Вам нужно пройти по дереву. Здесь может помочь E4X.

4. @PointedEars: Спасибо за советы. Я включил все, что мог.

5. @PointedEars плоский список будет проблемой, но я думаю, что могу использовать функцию, которую я написал в прошлом, для перемещения по дереву, используя рекурсию, чтобы углубиться в «многомерный массив», но я понятия не имею, сработает ли это.

Ответ №2:

Я подумал, что это было бы интересно сделать без стороннего анализатора, поэтому я создал простой:

 function parse(code)
{
  var obj = {},
      cur = obj,
      stack = [];

  code.replace(/[([^]] )]|([^[]*)/g, function (match, tagName, text) {
    if (tagName)
    {
      if (tagName.charAt(0) == "/")
      {
        /* end tag */
        cur = stack.pop();
      }
      else
      {
        /* start tag */
        stack.push(cur);
        cur = cur[tagName] = {};
      }
    }
    else
    {
      cur["#text"] = text;
    }
  });

  return obj;
}

var obj = parse(text);
  

Ответ №3: