2012-01-10 6 views
0

탭 분리 파일이 있는데 관련 하위 노드가있는 XML로 변환해야합니다. 파일은 다음과 같습니다. -TSV에서 Coldfusion을 사용하는 XML

Miscellaneous   
Ceremonial  
    Test1   
    Test2 
    Test3 
Sport  
    Athletics 
    Basketball 
    Biathlon  
    Boxing 
    Canoeing  
    Clay Pigeon Shooting  
    Climbing  
    Cricket 
    Cycling 
    Diving 
    Football  
    Football  
    Freefall  
    Gliding 
    Hill Walking  
    Hockey 
    Martial Arts  
     Karate 
     Judo 
     Jujitsu 
    Modern Pentathlon 
    Mountaineering 
    Orienteering  
    Parachuting 
    Paragliding 
    Parascending  
    Polo  
    Rugby 
    Rugby League  
    Rugby Union 
    Soccer 

나는 3 단계 즉 마샬 아트에서 멈추었습니다.

다음은 내가 작성한 코드로 2 레벨까지 작동합니다.

<cfif structKeyExists(form, "xlsfile") and len(form.xlsfile)> 

<!--- Destination outside of web root ---> 
<cfset dest = getTempDirectory() /> 
<cffile action="upload" destination="#dest#" filefield="xlsfile" result="upload" nameconflict="makeunique"> 
<cfset theFileUploaded = upload.serverDirectory & "/" & upload.serverFile /> 
<cffile action="read" file="#theFileUploaded#" variable="theFile"> 
<cfset CrLf = chr(10) & chr(13) /> 
<cfset counter = 0 /> 

<cfset dataStr = structNew()> 
<cfset isRoot = false> 
<cfset tabCount = 0> 
<cfset counter = 1> 
<cfset childCounter = 1> 
<cfset previousResult = 1> 

<cfloop list="#theFile#" index="run" delimiters="#CrLf#"> 
    <!--- The test value. ---> 
    <cfset strTest = #Rtrim(run)# /> 
    <!--- The instance counter. ---> 
    <cfset intCount = 0 /> 
    <!--- Get the initial position. ---> 
    <cfset intPosition = Find(chr(9), strTest, 0) /> 
    <!--- Keep searching till no more instances are found. ---> 
    <cfloop condition="intPosition"> 
     <!--- Increment instance counter. ---> 
     <cfset intCount = (intCount + 1) /> 
     <!--- Get the next position. ---> 
     <cfset intPosition = Find(chr(9), strTest, (intPosition + Len(chr(9)))) /> 
    </cfloop> 

    <!--- Output the number of target instances. 
    <cfoutput> 
     --- #intCount# --- <br/> 
    </cfoutput>   ---> 
    <cfset childNode = "Child" & counter> 
    <cfdump var="#intCount-tabCount#"> 
    <!--- Root ---> 
    <cfif intCount eq 0> 
     <cfset dataStr.root = strTest> 
     <cfset tabCount = intCount> 
    <!--- Child at level 1 ---> 
    <cfelseif tabCount eq 0 > 
     <cfset tabCount = intCount> 
     <cfset dataStr[childNode] = StructNew()> 
     <cfset dataStr[childNode].root = strTest> 
    <!--- Child at sub levels ---> 
    <cfelseif ((intCount-tabCount) eq 0) or ((intCount-tabCount) eq 1)> 

     <cfif previousResult eq 0 and intCount-tabCount eq 1> 
      <cfdump var="#strTest#"> 
     </cfif> 

      <cfset tabCount = intCount>   
      <cfset tabCount = intCount> 
      <cfset subChildNode = "Child" & childCounter> 
      <cfset dataStr[childNode][subChildNode] = strTest>  
      <cfset childCounter = childCounter+1> 
      <cfset previousResult = intCount-tabCount> 

    <cfelseif previousResult eq 0> 
     <cfset counter = counter+1> 
     <cfset childNode = "Child" & counter> 
     <cfset childCounter = 1> 
     <cfset tabCount = intCount> 
     <cfset dataStr[childNode] = StructNew()> 
     <cfset dataStr[childNode].root = strTest>      
    <cfelse> 
     <cfset counter = counter+1> 
     <cfset childNode = "Child" & counter> 
     <cfset childCounter = 1> 
     <cfset tabCount = intCount> 
     <cfset dataStr[childNode] = StructNew()> 
     <cfset dataStr[childNode].root = strTest> 
    </cfif> 


</cfloop> 

<cfdump var="#dataStr#"> 

+1

정확히 무엇이 문제입니까? – Yisroel

답변

6

난 당신이 개념에 어려움을 겪고있는 가정이 답변을거야 - 그것은 3보다 수준 냄비하게 해결하기 위해 무엇을

는 사람이 말해 주 시겠어요 재귀계층 구조 (부모 - 자식) 데이터 구조, 정확히 무엇이 문제인지 명확히하지 않았으므로

첫 번째 두 레벨을 얻으려면 루프 내에서 루프가 좋지만, 코드가 복잡해지면 관리가 어려워지고 ... txt 파일에 갑자기 네 번째 또는 다섯 번째 레벨의 어린이 - 코드를 지속적으로 업데이트해야합니다.

이 해결책은 재귀 함수를 작성하는 것입니다. 즉, 자신을 호출하는 함수.

먼저 "루트"XML 노드가 될 기본 구조체를 설정하고, 우리가 임의로 루트 문서 "카테고리"전화 할게 :

<cfset XmlDoc = XmlNew(true) /> 
<cfset XmlDoc.xmlRoot = XmlElemNew(XmlDoc, "Categories") /> 

이의는 또한 TXT의 내용을 읽어하자를

<cffile action="read" file="c:\workspace\nodes.txt" variable="nodes"> 

물론, txt 파일은 어디에서 올 수 있습니다, 그래서 당신은 조정에 나는 떠날 것이다 : (당신이 어떤 위에 제공 한 하나는 부모 - 투 - 아이의 계층 구조를 반영하는 탭입니다) 파일 , 단지 우리가 "nodes"라는 컨텍스트를 포함하는 변수로 끝난다는 점에 유의하십시오. 위의 탭이 달린 txt 파일.

다음으로, 당신은 당신이 기입하는 새로운 기능으로, 루트 및 파싱 만족, 시작, 현재 노드 (와 함께 해당 xmldoc를 전달하는 것입니다려고 :

<cfset parseNodes(XmlDoc, XmlDoc['Categories'], nodes) /> 

이제 'nodes'변수를 처리하는 재귀 함수를 작성하고, 발견 한 내용을 xml 요소로 변환 한 다음 시작으로 전달 된 루트 xml 요소 (카테고리)에 연결합니다. 이 기능을 자세히 설명하기 전에 나는 당신에게 모든 것을 터뜨리기 전에 :

<cffunction name="parseNodes" returntype="string"> 
    <cfargument name="rootXml" type="xml" required="true" /> 
    <cfargument name="parentNode" type="xml" required="true" /> 
    <cfargument name="content" type="string" required="true" /> 
    <cfargument name="level" type="numeric" required="false" default="0" /> 

인수 1은 xml 노드 생성에 필요한 것으로서 재귀 호출을 통해 계속 전달할 루트 xml 문서입니다 (XmlElemNew() 통해)

인수 2는 자식을 연결할 부모 xml 노드입니다.

인수 3은 처리 중에 먹는 순간에 볼 수있는 현재 내용 (구문 분석 된 탭 txt 파일의 내용)입니다.

인수 4는 우리가 현재 부모 - 자식 계층에서 "계층"을 추적하는 데 사용하는 표식입니다. 먼저 parseNodes() 함수를 호출했을 때 인수를 지정하지 않았으므로 최상위 레벨 (0)에 있습니다.

<cfset var thisLine = "" /> 
<cfset var localContent = arguments.content /> 

우리가 실수로 우리가 우리 자신에 재귀 적으로 암시 적으로 글로벌로 변환 CF 값을 덮어 쓰지 않아도 우리는 일부 지역 바르를 설정합니다.

<cfloop condition="#Len(localContent)#"> 

우리는 다음 조건에 루프를 시작합니다 : 루프를 localContent 변수에 더 이상 길이 없을 때까지. 재귀 적으로 호출 할 때 우리는 이미 처리 한 내용을 계속해서 "먹어야"할 것이기 때문에 우리가 들어오고 나가는 동안 계속해서 다시 처리 할 수 ​​없기 때문입니다 재귀 함수 호출.

<cfset thisLine = ListGetAt(localContent, 1, Chr(13) & Chr(10)) /> 

줄 바꿈 문자로 새 줄을 사용하여 txt 파일의 첫 번째 줄을 가져옵니다.

<cfif CountIt(thisLine, chr(9)) eq arguments.level> 

여기서는 처리중인 현재 행에서 발견 된 탭의 수를 계산합니다. CountIt() 함수는 CFLib.org에서 사용할 수있는 또 다른 외부 UDF입니다. 나는 최종 코드 미리보기에서 그것을 아래에 포함시킬 것이다. 우리는 작업중인 현재 레벨이 상위 - 하위 하이라이팅의 올바른 위치와 일치하는지 확인하기 위해 탭 수를 계산합니다. 따라서 예를 들어, 우리가 루트 (0)에 있고 1 탭을 계산하면 바로 알 수 있습니다. 우리는 올바른 수준이 아니므로 재귀해야합니다.

<cfset arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)+1] = XmlElemNew(arguments.rootXml, '#Replace(Trim(thisLine),' ','_','ALL')#') /> 
<cfset arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)].XmlText = Trim(thisLine) /> 

우리는 우리가 올바른 수준에 결정했습니다, 그래서 우리는 XmlChildren 배열에 새 요소를 추가하고 (thisLine에서 개최) 우리가 분석 값과 동일한의의 XmlName을 설정합니다. 사실 XmlElemNew() 함수가 호출되면 안전을 위해 thisLine을 트림 (trim) (thisLine)하고 빈 칸을 밑줄로 변환 할 때 XML 요소의 이름에 공백이 없으므로 (예 : <My Xml Node>은 오류가 발생합니다) . 다시 처리되지 않도록 우리가, 우리가 처리 한 당신의 txt 파일의 내용의 라인을 "먹어"여기서 여기

<cfset localContent = ListDeleteAt(localContent, 1, chr(10) & chr(13)) /> 

이다. 콘텐츠를 목록으로 다시 처리하고 (CRLF를 구분 기호로 사용) 첫 번째 항목 (맨 위)을 삭제합니다.

지금, 다음 두 줄은 우리가 우리를 결정할 경우 어떻게 부모 - 자식 계층의 정확한 수준에없는 무엇인가 : 우리가 결정 여기

<cfelseif CountIt(thisLine, chr(9)) gt arguments.level> 

    <cfset localContent = parseNodes(arguments.rootXml, arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)], localContent, arguments.level + 1) /> 

이 현재 행에있는 탭의 수 은 우리가 작업하고있는 수준보다 큰이므로 회귀해야합니다. 이는 다음 줄에서 발생합니다. 우리가 이미있는 parseNodes() 함수가 다시 호출되지만 매개 변수가 약간 업데이트됩니다.

  1. 우리는 여전히 루트 XML 문서를 전달합니다.
  2. 이제 가장 최근에 생성 된 자식 요소를 새 루트로 전달합니다.
  3. 우리는 우리의 현재 TXT 내용을 전달
  4. 우리가 내 도착을 나타내는, 우리는 hierachy 플러스 1의 현재 수준에 통과
  5. (기억, 이것은 우리가 가서 우리가 "먹어"하나입니다) 함수의 본문이 다시 올바른 수준에서 작동하고 있습니다.

마지막으로 가장 중요한 점은 메서드를 반환하면 localContent 변수이 업데이트된다는 점입니다. 이건 중요하다! 함수에 대한 재귀 호출도 구문 분석 된 txt 파일을 "먹"습니다. 따라서 각 외부 호출이 최신 구문 분석 (및 사용 된) 내용으로도 작동하는지 확인하는 것이 중요합니다.

마지막 조건은 탭 수가 현재 티어보다 작은 경우 실행됩니다. 즉, 현재 재귀 적 반복을 종료하고 부모에게 반환해야하며 "먹은 것"콘텐츠를 반환해야 함을 의미합니다 지금까지이 반복에서 처리 :

<cfelse> 

     <cfreturn localContent /> 

    </cfif> 

</cfloop> 

<cfreturn '' /> 

</cffunction> 

는 이제 자신을 재귀 적으로 호출하고 부모 - 자식 관계의 계층의 수를 처리 할 수있는 하나의 기능을 가지고있다.

COMPLETED CODE

<cfset nl = chr(10) & chr(13) /> 
<cfset tab = chr(9) /> 

<cfscript> 
//@author Peini Wu ([email protected]) 
function CountIt(str, c) { 
    var pos = findnocase(c, str, 1); 
    var count = 0; 

    if(c eq "") return 0; 

    while(pos neq 0){ 
     count = count + 1; 
     pos = findnocase(c, str, pos+len(c)); 
    } 

    return count; 
} 
</cfscript> 

<cffunction name="parseNodes" returntype="string"> 
    <cfargument name="rootXml" type="xml" required="true" /> 
    <cfargument name="parentNode" type="xml" required="true" /> 
    <cfargument name="content" type="string" required="true" /> 
    <cfargument name="level" type="numeric" required="false" default="0" /> 

    <cfset var thisLine = "" /> 
    <cfset var localContent = arguments.content /> 

    <!--- we will loop until the localContent is entirely processed/eaten up, and we'll trim it as we go ---> 
    <cfloop condition="#Len(localContent)#"> 

     <cfset thisLine = ListGetAt(localContent, 1, nl) /> 

     <!--- handle everything at my level (as specified by arguments.level) --->  
     <cfif CountIt(thisLine, tab) eq arguments.level> 

      <cfset arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)+1] = XmlElemNew(arguments.rootXml, '#Replace(Trim(thisLine),' ','_','ALL')#') /> 

      <cfset arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)].XmlText = Trim(thisLine) />   

      <!--- this line has been processed, so strip it away ---> 
      <cfset localContent = ListDeleteAt(localContent, 1, nl) /> 

     <!--- the current line is the next level down, so we must recurse upon ourselves --->   
     <cfelseif CountIt(thisLine, tab) gt arguments.level> 

      <cfset localContent = parseNodes(arguments.rootXml, arguments.parentNode.XmlChildren[ArrayLen(arguments.parentNode.XmlChildren)], localContent, arguments.level + 1) /> 

     <!--- the current level is completed, and the next line processed is determined as a "parent", so we return what we have processed thus far, allowing the recursed parent function 
     to continue processing from that point --->  
     <cfelse> 

      <cfreturn localContent /> 

     </cfif> 

    </cfloop> 

    <!--- at the very end, we've processed the entire text file, so we can simply return an empty string ---> 
    <cfreturn '' /> 
</cffunction> 

<cffile action="read" file="c:\workspace\cf\sandbox\nodes.txt" variable="nodes"> 

<cfset XmlDoc = XmlNew(true) /> 
<cfset XmlDoc.xmlRoot = XmlElemNew(XmlDoc, "Categories") /> 
<cfset parseNodes(XmlDoc, XmlDoc['Categories'], nodes) /> 

<cfdump var=#xmlDoc#> 

<textarea rows="40" cols="40"> 
<cfoutput>#xmlDoc#</cfoutput> 
</textarea> 

주의

당신은 당신이 원하는 최종 XML의 형식으로 귀하의 질문에 명확하게하지 않았기 때문에이 과정이 여기에 다소 중복 구조를 생성 노드의 값과 일치하는 노드 (매우 유용하지는 않음) :

<?xml version="1.0" encoding="UTF-8"?> 
<Categories> 
    <Miscellaneous>Miscellaneous</Miscellaneous> 

이것은 아마도 당신이 길 아래로 가고 싶어하는 것이 아니지만, 더 이상 명시하지 않는 한, 예제를 단순하게 유지하기위한 가정을 추측해야합니다.

+0

와우. 나는 이것을 두 번이나 투표 할 수 있었다. – Yisroel

+0

Hey Shawan, 그것은 약간의 설명이었다 !!! 건배 남자! – nasaa

관련 문제