【问题标题】:Converting Hierarchical Data to XML in Flex在 Flex 中将分层数据转换为 XML
【发布时间】:2013-04-05 13:12:53
【问题描述】:

如何在 Flex 中将平面/分层数据转换为 XML 格式。 以下是我的分层数据:(表格格式)

Asia  India  Chennai  TN  Category1 Product1 100
Asia  India  Mumbai   MH  Category1 Product1 100
Asia  India  Calcutta CT  Category1 Product1 100
Asia  India  Calcutta CT  Category2 Product2 200
EMEA  UK     London   LN  Category3 Product1 123    
EMEA  UK     London   LN  Category3 Product2 455    
EMEA  UK     Reading  RN  Category1 Product1 500    
EMEA  UK     Reading  RN  Category1 Product2 430

        I need to format/convert this to XML format so that I can populate that resulting xml as dataprovider to a Tree control.
       Asia
         India
             Chennai
                TN
                  Category1
                     Product1
                          100
             Mumbai
                MH
                  Category1
                     Product1
                          100   
                                such a tree structure.                      

【问题讨论】:

  • 源文件中使用了哪些分隔符?你能把你的源文件上传到任何服务器并给我们一个链接吗?

标签: xml apache-flex


【解决方案1】:

我会制作一个简单的转换器来获得真正的 XML 结构。解决方案的复杂性取决于您拥有的源文本。

这是我的实现。

假设您的分层数据结构良好,并且每个元素之前都有一定数量的空格。在我的例子中,我使用 4 个空格作为一级转换。

我从一个文本文件加载数据,如下所示:

    Asia
        India
            Chennai
                TN
                    Category1
                        Product1
                            100
            Mumbai
                MH
                    Category2
                        Product2
                            200
                    Category3
                        Product3
                            300                  
                        Product4
                            400

然后我遍历每个字符串并分析其级别。 生成的树如下所示:

// 应用

<?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.events.FlexEvent;

        private const SPACEDELIMITER:int = 4;
        private var loader:URLLoader;
        private var ar:Array = new Array();
        [Bindable]private var data:XML = <Root/>;
        private var inputStr:String;

        protected function init(event:FlexEvent):void
        {
            loader = new URLLoader(new URLRequest("com/treexml/tree.txt"));
            loader.dataFormat = URLLoaderDataFormat.TEXT;
            loader.addEventListener(Event.COMPLETE, completeHandler);
        }

        private function completeHandler(event:Event):void
        {
            inputStr = URLLoader(event.target).data;
            parseString();
        }

        private function parseString():void
        {
            var levels:Array = new Array();
            ar = inputStr.split("\r\n");
            var reg:RegExp = /[a-z0-9]/gi;

            var globalShift:int = String(ar[0]).search(reg);

            for (var i:int = 0; i < ar.length; i++)
            {
                var item:String = ar[i];
                var shift:int = item.search(reg); //amount of witespaces before the text
                var level:int = (shift - globalShift)/SPACEDELIMITER; //level of the node
                var label:String = item.substring(shift, item.length);

                levels[level] = i; //id of the last element for the given level 

                var node:XML = new XML();
                node = <child id = {i} label = {label}/>;

                if (level == 0)
                    data.appendChild(node);
                else
                    data..child.(@id == levels[level - 1]).appendChild(node);
            }
        }

    ]]>
</fx:Script>

<mx:Tree width="250" 
         height="400" 
         dataProvider="{data.child}"
         labelField="@label"/>

</s:Application>

//编辑

如果您的数据未通过空格对齐,请尝试使用其他版本的 parseString 函数。

假设您的数据如下所示:

Asia India Chennai TN Category1 Product1 100
Asia India Mumbai MH Category1 Product1 100
Asia India Calcutta CT Category1 Product1 100
Asia India Calcutta CT Category2 Product2 200
EMEA UK London LN Category3 Product1 123
EMEA UK London LN Category3 Product2 455
EMEA UK Reading RN Category1 Product1 500
EMEA UK Reading RN Category1 Product2 430

函数是:

            private function parseString():void
{
    ar = inputStr.split("\r\n");
    var map:Dictionary = new Dictionary();
    var delimiter:String = "***";
    var id:int = 0;
    for (var i:int = 0; i < ar.length; i++)
    {
        if(ar[i].length){//if it's not an empty string
            var itemArray:Array = ar[i].replace(/\s{2,}/g, ' ').split(" ");//collapse multiple spaces as one using RegEx
            var key:String = "";
            var prevkey:String = "";

            for (var j:int = 0; j< itemArray.length; j++)
            {
                prevkey = key;
                key += itemArray[j] + delimiter;

                if (map[key] == null)
                {
                    map[key] = id;

                    var node:XML = <child id = {id} label = {itemArray[j]}/>;

                    if (j == 0)
                        data.appendChild(node);
                    else
                        data..child.(@id == map[prevkey]).appendChild(node);

                    id++;
                }
            }
        }
    }
}

【讨论】:

  • 感谢您的真诚尝试。但我将获得动态数据,对于大型数据收集而言,这些数据与每个数据的空白对齐是不可能的。我需要一种动态方法。
  • 您能解释一下哪些符号将用于描述层次结构吗?哪个功能可以让我们区分孩子和父母?也许我可以改进我的方法。你有可以解析的真实文本吗?
  • 我是否理解正确,诸如“EMEA UK London LN Category3 Product2 455”之类的字符串是真实数据?那么它可能比我的方法更容易。
  • 我为我的答案添加了另一种方法。也许它会帮助你。
  • 正在为 Xcelsius 应用程序(SAP 产品)构建自定义组件。我获得的数据将来自 Excel,我将作为“2D 数组”获得,我已在上述问题中发布。我需要处理数据,使其满足两个标准:1.)数据需要过滤以避免重复行。 2.)过滤后的数据需要填充到树组件中。如上问题。
【解决方案2】:

这是使用递归函数形式this answer 的一种不太有效的方法:

var data:String = '';//this is just to keep the data in the editor, you would dinamically load this usually
data += 'Asia  India  Chennai  TN  Category1 Product1 100\n';
data += 'Asia  India  Mumbai   MH  Category1 Product1 100\n';
data += 'Asia  India  Calcutta CT  Category1 Product1 100\n';
data += 'Asia  India  Calcutta CT  Category2 Product2 200\n';
data += 'EMEA  UK     London   LN  Category3 Product1 123\n';    
data += 'EMEA  UK     London   LN  Category3 Product2 455\n';    
data += 'EMEA  UK     Reading  RN  Category1 Product1 500\n';    
data += 'EMEA  UK     Reading  RN  Category1 Product2 430\n';

trace(cleanNodes(parseTableString(data).*));

function parseTableString(str:String):XML{
    var lines:Array = str.split('\n');
    var xml:XML = <root />;
    for(var i:int = 0; i < lines.length; i++){
        //collapse multiple spaces to 1 using RegEx, then split
        var nodes:Array = lines[i].replace(/\s{2,}/g, ' ').split(' ');
        for(var j:int = 0 ; j < nodes.length; j++){
            if(nodes[j].length){
                var node:String = '<item name="'+nodes[j]+'"/>';
                if(j > 0){//if the node has parents
                    var pre:String = '';//create enclosing tags
                    var post:String = '';
                    for(var k:int = 0 ; k < j; k++){//build up parent hierarchy
                        pre += '<item name="'+nodes[k]+'">';
                        post += '</item>';
                    }
                    node = pre+node+post;//concatenate as a full node
                    xml.appendChild(new XML(node));//add full node (will add duplicates)
                }
            }
        }
    }
    return xml;
}

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 = child.@name;
        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;  
}

哪些输出:

<root>
  <item name="Asia">
    <item name="India">
      <item name="Chennai">
        <item name="TN">
          <item name="Category1">
            <item name="Product1">
              <item name="100"/>
            </item>
          </item>
        </item>
      </item>
      <item name="Mumbai">
        <item name="MH">
          <item name="Category1">
            <item name="Product1">
              <item name="100"/>
            </item>
          </item>
        </item>
      </item>
      <item name="Calcutta">
        <item name="CT">
          <item name="Category1">
            <item name="Product1">
              <item name="100"/>
            </item>
          </item>
          <item name="Category2">
            <item name="Product2">
              <item name="200"/>
            </item>
          </item>
        </item>
      </item>
    </item>
  </item>
  <item name="EMEA">
    <item name="UK">
      <item name="London">
        <item name="LN">
          <item name="Category3">
            <item name="Product1">
              <item name="123"/>
            </item>
            <item name="Product2">
              <item name="455"/>
            </item>
          </item>
        </item>
      </item>
      <item name="Reading">
        <item name="RN">
          <item name="Category1">
            <item name="Product1">
              <item name="500"/>
            </item>
            <item name="Product2">
              <item name="430"/>
            </item>
          </item>
        </item>
      </item>
    </item>
  </item>
</root>

我个人认为@Anton 的回答更好,因为它只在需要时添加节点(而不是我的创建重复,然后删除它们)。我还添加了一个正则表达式来将多个空格合并为一个到他的答案中。

【讨论】:

  • 非常感谢。这按预期工作,但它将墨西哥下的墨西哥城视为其孩子的“城市”。这意味着它忽略它表示重复。
  • 另外还有一个问题,如果一个地区有两个数值数据,那么它必须在一个父级之下。现在它正在考虑一个数值数据作为最后一个数据的父级。例如印度-> Chennai->Category 1->100->200 我计划 100 作为我的销售额和 200 我在 Chennai 地区类别 1 下的利润数据,它必须显示 100,200 作为 Chennai 的孩子,而不是 200 作为 100 的孩子。
  • @Flexiflex re:Mexico:很好地演示了为什么空格是不好的分隔符。这就是为什么“Mexico City”也被拆分为 ["Mexico","City"],因此额外的节点。 Mexico Mexico Mexico City MC Category2 Product1 200 变为 &lt;item name="Mexico"&gt; &lt;item name="Mexico"&gt; &lt;item name="Mexico"&gt; &lt;item name="City"&gt; &lt;item name="MC"&gt; &lt;item name="Category2"&gt; &lt;item name="Product1"&gt; &lt;item name="200"/&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt;...
  • Mexico Mexico Mexico_City MC Category2 Product1 200 变为 &lt;item name="Mexico"&gt; &lt;item name="Mexico"&gt; &lt;item name="Mexico_City"&gt; &lt;item name="MC"&gt; &lt;item name="Category2"&gt; &lt;item name="Product1"&gt; &lt;item name="200"/&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; &lt;/item&gt; 更接近您想要的,但尽管您看到的是 Mexico_City。您可以进行额外检查,如果标签中包含“_”,您可以在解析行之后用空格替换它,但它仍然是 hacky。 ...
  • ...您是否可以控制来自服务器的数据作为表格数据?如果您可以控制用作分隔符的字符,这将是更清洁的解决方案。您可以使用 \t(如在 csv 或 tsv 中)之类的东西。在这种情况下,表格数据格式良好的事实并没有太大帮助。反正墨西哥不会错位吗?
【解决方案3】:

如果转换为 XML 的唯一原因是用作 Tree 数据提供者,那么您不需要这样做。一种更有效的方法是使用树数据描述符。这使您可以使数据提供者保持平坦,而描述符可以让您描述其结构,以便树知道哪些对象是彼此的子对象。 http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-7b69.html

【讨论】:

  • 我对最终解决方案很感兴趣。请让我们看看。
  • @Peter Hall :我之前检查过链接。他们静态地对数组详细信息进行了硬编码。在我的情况下,我会动态获取数组,所以在这种情况下该怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
相关资源
最近更新 更多