2013-04-08 2 views
0

Я уже ссылался на эту следующую ссылку, но не смог найти решение для моего сценария.Как преобразовать XMLList в XML в FLEX

Actionscript 3 - How can i convert from XMLList to XML?

Моя переменная XML выглядит следующим образом:

private var checkXml:XML = new XML(
<universe> 
<item name="cat 2"> 
    <item name = "All"/> 
    <item name = "item 1"/> 
    <item name = "item 2"/> 
    </item> 
    <item name="cat 2"> 
    <item name = "All"/> 
    <item name = "item 3"/> 
    <item name = "item 4"/> 
    <item name = "item 5"/> 
    <item name = "item 5"/> 
    </item> 
    <item name="cat 3"> 
    <item name = "All 33"/> 
    <item name = "item 34"/> 
    <item name = "item 44"/> 
    </item> 
</universe>); 

Я использую функцию фильтра для удаления повторяющихся значений в выше XML как:

private function filter(xmlSample:XML):XMLList 
{ 
    var seen:Object={}; 
    return [email protected](!seen[valueOf()]&&(seen[valueOf()]=true)); 
} 

который возвращает данные XMLList. Когда я использую это, чтобы получить формат XML как:

var thisXml:XMLList = filter(checkXml); 
Alert.show(thisXml.toXMLString()); 

я не получаю данные в формате XML, я получаю это так .:

 cat 2 
    All 
    item 1 
    item 2 
    item 3 
    item 4 
    item 5 
    cat 3 
    All 33 
    item 34 
    item 44 

Как получить то же самое в формате XML в Flex, как у моей переменной XML «checkXml», так что. Я могу сохранить все родительские узлы и дочерние узлы, поскольку это, таким образом, удаляет дубликаты.

+1

Я не думаю, что вы можете достичь этого с помощью функции фильтра, которую вы в настоящее время имеете. Теперь вы выбираете только атрибуты «name», в то время как вы должны выбирать элементы «item». Думаю, вам нужно написать функцию рекурсивного фильтра, которая отслеживает посещаемые и удаленные узлы. –

ответ

1

Вот краткое описание:

function clean(xml:XML):XML{ 
    var paths:Dictionary = new Dictionary(true);//keep track of paths 
    var result = new XML("<"+xml.localName()+" />");//make new xml 
    for each(var child:XML in xml.*){//travers 1st level 
     var path:String = child.parent().localName()+"/"+child.localName()+"@"[email protected];//get a path (I formatted it like) 
     if(!paths[path]) {//if it's a new node 
      paths[path] = child;//store it in the dictionary 
      result.appendChild(child.copy());//add it to the result 
     }else {//otherwise copy children 
      for each(var clone:XML in child.*)//check for duplicates, otherwise insert, NOTE this does not merge child nodes yet :(
       if(result[child.localName()][0].*.(@name == [email protected]).length() == 0) result[child.localName()][0].appendChild(clone); 
     } 
    } 
    trace(result.toXMLString()); 
    return result; 
} 

С базовым xml у вас есть эта работа, но она не очень гибкая. Он должен объединить дочерние узлы для дубликатов и, возможно, должен быть рекурсивным, но у меня нет времени.

Обновление: У меня есть еще две версии для вас.Тот, который идет в дочерние узлы уровня:

function clean(xml:XML):XML{ 
    var paths:Dictionary = new Dictionary(true);//keep track of 
    var result = new XML("<"+xml.localName()+" />"); 
    for each(var child:XML in xml.*){ 
     var path:String = child.parent().localName()+"/"+child.localName()+"@"[email protected]; 
     if(!paths[path]) { 
      paths[path] = child; 
      result.appendChild(child.copy()); 
     }else 
      for each(var clone:XML in child.*) 
       if(result[child.localName()][0].*.(@name == [email protected]).length() == 0) result[child.localName()][0].appendChild(clone); 
       else result[child.localName()][0].*.(@name == [email protected])[0].appendChild(clone.*); 
    } 
    return result; 
} 

так что XML, как это:

var data:XML = <universe> 
<item name="cat 2"> 
    <item name = "All"> 
     <item name="item child 1" /> 
     <item name="item child 3"> 
      <item name="item grandchild 1" /> 
     </item> 
    </item> 
    <item name = "item 1"> 
     <item name = "item child 1" /> 
    </item> 
    <item name = "item 2"/> 
    </item> 
    <item name="cat 2"> 
    <item name = "All"> 
     <item name="item child 2" /> 
     <item name="item child 3"> 
      <item name="item grandchild 2" /> 
     </item> 
    </item> 
    <item name = "item 3"/> 
    <item name = "item 4"/> 
    <item name = "item 5"/> 
    <item name = "item 5"/> 
    </item> 
    <item name="cat 3"> 
    <item name = "All 33"/> 
    <item name = "item 34"/> 
    <item name = "item 44"/> 
    </item> 
</universe>; 

производит вывод так:

<universe> 
    <item name="cat 2"> 
    <item name="All"> 
     <item name="item child 1"/> 
     <item name="item child 3"> 
     <item name="item grandchild 1"/> 
     </item> 
     <item name="item child 2"/> 
     <item name="item child 3"> 
     <item name="item grandchild 2"/> 
     </item> 
    </item> 
    <item name="item 1"> 
     <item name="item child 1"/> 
    </item> 
    <item name="item 2"/> 
    <item name="item 3"/> 
    <item name="item 4"/> 
    <item name="item 5"/> 
    </item> 
    <item name="cat 3"> 
    <item name="All 33"/> 
    <item name="item 34"/> 
    <item name="item 44"/> 
    </item> 
</universe> 

Обратите внимание, что атрибуты в корневом узле теряются, если таковые имеются. Возможно, лучший вариант по-прежнему использовать рекурсию, например, так:

function cleanNodes(nodes:XMLList):XML{ 
    var parent:XML = nodes.parent(); 
    var result:XML = new XML("<"+parent.localName()+" />");//copy parent node name 
    for each(var a:XML in parent.attributes()) result['@'+a.name()] = parent.attribute(a.name());//and attributes 
    //merge duplicates at one level 
    var found:Dictionary = new Dictionary(true); 
    for each(var child:XML in nodes){ 
     var name:String = [email protected]; 
     if(!found[name]) { 
      found[name] = child; 
      result.appendChild(child); 
     }else{//merge 
      found[name].appendChild(child.*); 
     } 
    } 
    //recurse 
    for each(var kid:XML in result.*){//for each child node 
     if(kid.*.length() > 0){//if it has children 
      var clean:XML = cleanNodes(kid.*);//get a clean copy of each child node 
      delete result.*[kid.childIndex()];//remove the original 
      result.appendChild(clean);  //add the cleaned one (replace) 
     } 
    } 
    return result; 
} 

Я не уверен, если это чистейшее/самое элегантное решение, но оно работает:

trace(cleanNodes(data.*)); 

производит:

<universe> 
    <item name="cat 2"> 
    <item name="item 2"/> 
    <item name="item 3"/> 
    <item name="item 4"/> 
    <item name="item 5"/> 
    <item name="All"> 
     <item name="item child 1"/> 
     <item name="item child 2"/> 
     <item name="item child 3"> 
     <item name="item grandchild 1"/> 
     <item name="item grandchild 2"/> 
     </item> 
    </item> 
    <item name="item 1"> 
     <item name="item child 1"/> 
    </item> 
    </item> 
    <item name="cat 3"> 
    <item name="All 33"/> 
    <item name="item 34"/> 
    <item name="item 44"/> 
    </item> 
</universe> 

Узлы с одинаковыми именами были рекурсивно свернуты (например, «Все» и «Детский ребенок 3»), но, к сожалению, узел упорядочивает путь немного.

+0

Спасибо за ответ. Да, это работает, и это то, что я хотел. – Flexiflex

+0

@Flexiflex Рад помочь. Я также добавил рекурсивную версию –

0

Если вы хотите, чтобы фильтровать только детский слой попробовать это:

enter image description here

<?xml version="1.0" encoding="utf-8"?> 
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" 
      xmlns:mx="library://ns.adobe.com/flex/mx" 
      minWidth="955" minHeight="600" creationComplete="init(event)"> 
<fx:Script> 
    <![CDATA[ 
     import mx.controls.Alert; 
     import mx.events.FlexEvent; 
     private var checkXml:XML = new XML(
      <universe> 
      <item name="cat 2"> 
       <item name = "All"/> 
       <item name = "item 1"/> 
       <item name = "item 2"/> 
       </item> 
       <item name="cat 2"> 
       <item name = "All"/> 
       <item name = "item 3"/> 
       <item name = "item 4"/> 
       <item name = "item 5"/> 
       <item name = "item 5"/> 
       </item> 
       <item name="cat 3"> 
       <item name = "All 33"/> 
       <item name = "item 34"/> 
       <item name = "item 44"/> 
       </item> 
      </universe>); 

     private function filter(xmlSample:XML):XMLList 
     { 
      var seen:Object={}; 
      return xmlSample.item.item.(!seen[@name.valueOf()]&&(seen[@name.valueOf()]=true)); 
     } 

     protected function init(event:FlexEvent):void 
     { 
      var thisXml:XMLList = filter(checkXml); 
      Alert.show(thisXml.toXMLString()); 
     } 

    ]]> 
</fx:Script> 

</s:Application> 

EDIT

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

 private function filter(xmlSample:XML):XMLList 
     { 
      var seen:Object={}; 
      var seen_child:Object={}; 
      return xmlSample.item.(!seen[@name.valueOf()]&&(seen[@name.valueOf()]=true)).item.(!seen_child[@name.valueOf()]&&(seen_child[@name.valueOf()]=true)); 
     } 

В этом случае фильтр вытирает второй узел «cat 2» и все дубликаты среди детей в остальных узлах.

EDIT2

Если вы хотите фильтровать оба слоя и не хотят потерять 2-мерную структуру, сделать что-то вроде этого:

 private function filter(xmlSample:XML):XMLList 
     { 
      var seen:Object={}; 
      var firstLevel:XMLList = xmlSample.item.(!seen[@name.valueOf()]&&(seen[@name.valueOf()]=true)); 

      var secondLevel:XMLListCollection = new XMLListCollection(); 

      var seen_child:Object={}; 

      for each (var xmlItem:XML in firstLevel) 
      { 
       var tempXMLList:XMLList = xmlItem.item.(!seen_child[@name.valueOf()]&&(seen_child[@name.valueOf()]=true)); 
       xmlItem.setChildren(tempXMLList); 
       secondLevel.addItem(xmlItem); 
      } 

      return secondLevel.source; 
     } 

Результат:

enter image description here

Смежные вопросы