ColdFusion - 如何遍历XML输出并添加到结构或数组?

问题描述:

我正在尝试创建章军官和他们各自的职位清单。数据来自通过Web服务访问的一系列XML键值对(Key:Member_Name,Value:Joe Member。Key:Position_Name,Value:President,等等。)给定章节的每个主管都有自己的Member_Name和Position_Name。ColdFusion - 如何遍历XML输出并添加到结构或数组?

我用仅会返回整个对象工作的API,所以我成立了一个数组来转换XML名称和持有的一切:

<cfset keyValue = xmlSearch(soapBody,"//*[local-name()='KeyValueOfstringanyType']") /> 

我的想法是,通过该数组循环,为关键MEMBER_NAME和Position_Name的所有实例,该值添加到一个结构:

<cfset chapterOfficers=structNew()> 
<cfloop index="i" from="1" to="#arrayLen(keyValue)#"> 
    <cfif keyValue[i].Key.xmlText EQ 'Member_Name'> 
     <cfset chapterOfficers.Name=keyValue[i].Value.xmlText> 
    </cfif> 
    <cfif keyValue[i].Key.xmlText EQ 'Position_Name'> 
     <cfset chapterOfficers.Position = keyValue[i].Value.xmlText> 
    </cfif> 
    <cfif keyValue[i].Key.xmlText EQ 'Term_Name'> 
     <cfset chapterOfficers.Term = keyValue[i].Value.xmlText> 
    </cfif> 
</cfloop> 

倾销这种结构给了我一个人的名字,那个人的位置,他们的任期漂亮整洁的小表 - 但只有一个(恰好是最后一个入口在XML文件中)。即使添加i = i + 1也没有任何影响 - 这几乎就像循环从最后开始,而不是继续。

我尝试添加的东西向结构,确实看透了一切循环的其他方式,但键/值对在一个不相关的顺序出来。我知道结构不是有序的,但我需要有一些方法来从XML输出中排序数据。我也尝试过各种其他循环,试图将一系列像这样的小结构添加到数组中。它的工作,但再次,只为那个人 - 没有真正的“循环”似乎发生!我可以同时看到所有我需要的信息 - 也许这是为了让我做错了什么?

谢谢大家提前,我感谢任何建议或在正确的方向轻推!

UPDATE:不知道这是否有帮助,但现在我在我正在使用的循环中交换了To和From的值,并将步骤设置为-1,并且它给了我名单上的第一个人。但仍然没有循环。

更新:谢谢彼得,这里是我一起工作的XML的例子:

<b:KeyValueOfstringanyType> 
        <b:Key>Member_Guid</b:Key> 
        <b:Value i:type="d:string">006e1c09-25f9-4178-86de-13c3e63200ce</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Member_Type</b:Key> 
        <b:Value i:type="d:string">Entity</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Member_Name</b:Key> 
        <b:Value i:type="d:string">Member, Joe</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Position_Guid</b:Key> 
        <b:Value i:type="d:string">02ae1c09-5779-4891-8cd1-05cf475cf5af</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Position_Type</b:Key> 
        <b:Value i:type="d:string">CommitteePosition</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Position_Name</b:Key> 
        <b:Value i:type="d:string">President</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Term_Guid</b:Key> 
        <b:Value i:type="d:string">044e1c09-a90b-495f-891f-afa13e653dee</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Term_Type</b:Key> 
        <b:Value i:type="d:string">CommitteeTerm</b:Value> 
       </b:KeyValueOfstringanyType> 
       <b:KeyValueOfstringanyType> 
        <b:Key>Term_Name</b:Key> 
        <b:Value i:type="d:string">2011-2012</b:Value> 
       </b:KeyValueOfstringanyType> 

重复对文件每章官。

更新:这里是我想出的代码。它做我想做它,但有更好的方法来做到这一点,我相信...

首先,我从SOAP响应的结果,“深入”到我需要的级别,和然后剥离出特定于XML的东西和使数据进入一个可用的数组:

<cfset soapBody = xmlParse(cfhttp.fileContent)> 
<cfset soapBody = soapBody['s:Envelope']['s:Body'].QueryResponse.QueryResult.Objects.anyType.Fields /> 
<cfset keyValue = xmlSearch(soapBody,"//*[local-name()='KeyValueOfstringanyType']") /> 

然后

<cfset chapterOfficers=arrayNew(2)> 
<cfset x=1> 
<cfset y=1> 

<cfloop index="i" from="1" to="#arrayLen(keyValue)#"> 
    <cfif keyValue[i].Key.xmlText EQ 'Member_Name'> 
     <cfset memberName = keyValue[i].Value.xmlText> 
     <cfset chapterOfficers[x][y]=#memberName#> 
     <cfset y=y+1> 
    </cfif> 
    <cfif keyValue[i].Key.xmlText EQ 'Position_Name'> 
     <cfset positionName = keyValue[i].Value.xmlText> 
     <cfset chapterOfficers[x][y]=#positionName#> 
     <cfset x=x+1> 
     <cfset y=1> 
    </cfif> 
    <cfif keyValue[i].Key.xmlText EQ 'Member_Guid'> 
     <cfset memberGuid = keyValue[i].Value.xmlText> 
     <cfset chapterOfficers[x][3]=#memberGuid#> 
    </cfif> 
</cfloop> 

我做一些其它的处理,检查变量存在,等等,然后输出军官姓名和他们各自的职位

<cfloop from="1" to="#arrayLen(chapterOfficers)#" index="x"> 
    <p> 
     <cfoutput><a href="OfficerDetail.cfm?sessionGuid=<cfoutput>#URL.sessionGuid#</cfoutput>&memberGuid=<cfoutput>#chapterOfficers[x][3]#</cfoutput>">#chapterOfficers[x][1]#</a></cfoutput><br /> 
     <cfoutput>#chapterOfficers[x][2]#</cfoutput><br /> 
    </p> 
</cfloop> 

我能够将Member_Guid添加到数组中并使用它,因此网站访问者可以点击一个人的姓名以查看更多详细信息(公司,电子邮件地址等)。这就是它!你怎么看?再次感谢您抽出时间,我非常感谢!

+0

的Structs只能包含一个组键 - 如果你需要一个额外的维度需要放置结构中的处于阵列(或使用查询记录) 。但是,您也不需要循环遍历所有的键,而是正确地处理XML - 在没有看到XML结构的情况下很难说更多的东西,所以发布XML数据的样本(带有任何被屏蔽/删除的敏感信息)。 – 2012-07-29 01:00:00

+0

彼得你好,非常感谢你的帮助。下面是一个XML示例: – daltec 2012-07-29 01:33:43

+0

@PeterBoughton我在原始文章中添加了一些XML示例。命名空间比这里展示的更广泛,但这有帮助吗?如果您需要更多信息,请告知我们,并非常感谢您的帮助。 – daltec 2012-07-29 01:42:26

这是我怎么可能会解决这个问题:

<cfset var ChapterOfficers = StructNew()> 
<cfset var CurMemberGuid = '' /> 

<cfloop index="local.CurPair" array=#keyValue#> 

    <cfif CurPair.Key.XmlText EQ 'Member_Guid' > 
     <cfset CurMemberGuid = CurPair.Value.XmlText /> 
     <cfset ChapterOfficers[CurMemberGuid] = StructNew() /> 
    <cfelse> 
     <cfset ChapterOfficers[CurMemberGuid][CurPair.Key.XmlText] = CurPair.Value.XmlText /> 
    </cfif> 

</cfloop> 

它利用现有的XmlSearch你所做的,并假定Member_Guid总是第一个键/值对。我已经使用了var/local作用域,假设这是进入一个函数内部(它可能应该是这样),但是如果不是这样,就把它们移除。

它使用结构体,因此查找特定的GUID很容易,但顺序不会被保留(尽管如果必要,您可以保留一个单独的数组来执行该操作),并且您不必记住哪个数组位置与哪个键。

如果你想基于其他字段查找,你也可以将数据转换成一个查询,像这样:

<cfset var ChapterOfficers = QueryNew('Member_Guid,Member_Type,Member_Name,Position_Guid,Position_Type,Position_Name,Term_Guid,Term_Type,Term_Name')> 
<cfset var CurRow = 1 /> 

<cfloop index="local.CurPair" array=#keyValue#> 

    <cfif CurPair.Key.XmlText EQ 'Member_Guid' > 
     <cfset QueryAddRow(ChapterOfficers) /> 
    </cfif> 

    <cfset QuerySetCell(ChapterOfficers,CurPair.Key.XmlText,CurPair.Value.XmlText) /> 

</cfloop> 

这维持秩序,让更广泛的查找更容易,也使得它是否容易你主要用途是直接输出到HTML。

我已经硬编码列键,但你也可以做一个预循环来整理那些第一,如果他们是可以改变的东西。


希望这一切都有道理?

+0

哇,彼得,这太棒了!我对你最初的例子中的嵌套结构非常满意 - 这使得在循环中输出成员信息变得更容易。它也帮助我建立链接。我在我的网站上的其他地方广泛使用了我的旧方法,但是您的方法更加高效和简洁,而且说实话,现在我可以看到它并且仔细考虑它。非常感谢!我必须承认,我仍然试图去处理你的第二个例子,但我现在正在试验它。再次感谢Peter,这真是太棒了,我非常感谢你的帮助和建议! – daltec 2012-07-29 22:38:20

ColdFusion的10或Railo 4,你可以使用Underscore.cfc library帮助清理您的解决方案了很多:

<cfscript> 
    soapBody = XmlParse(cfhttp.filecontent); 
    fields = xmlSearch(soapBody,"//*[local-name()='Fields']"); 
    chapterOfficers = _.map(fields, function (field) { 
     var officer = {}; 
     _.each(field.xmlChildren, function (KeyValueOfstringanyType) { 
      var key = KeyValueOfstringanyType['b:Key'].xmlText; 
      var value = KeyValueOfstringanyType['b:Value'].xmlText; 
      officer[key] = value; 
     }); 
     return officer; 
    }); 
</cfscript> 

<cfoutput> 
<cfloop array="#chapterOfficers#" index="officer"> 
    <a href="OfficerDetail.cfm?sessionGuid=#URL.sessionGuid#&memberGuid=#officer.Member_Guid#">#officer.Member_Name#</a> 
    #officer.Position_Name#<br /> 
</cfloop> 
</cfoutput> 

现在是不是更好?我不确定你的SOAP响应是什么样的,但你应该能够调整xmlSearch()以匹配KeyValueOfstringanyType的父元素。我还为您删除了所有不必要的cfoutputs。另外,我建议切换到JSON而不是XML。这很容易解析。

(声明:我写的Underscore.cfc库)

+0

嗨,Russ看起来更容易处理 - 如果我们转向CF10,我会给它一个镜头。谢谢!感谢JSON的提示,我很感激。 – daltec 2012-08-04 20:37:38