Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [XML] Rekursywne generowanie i parsowanie XML-RPC
Forum PHP.pl > Forum > PHP
kiler129
Witajcie!
Szukałem gotowej biblioteki - znalazłem kilka ale albo wolne, albo nie działają jak powinny. Postanowiłem napisać własną, niestety napotkałem problem.
O ile samo generowanie XML`a nie sprawia problemu o tyle gdy jego struktura ma być bardziej zagnieżdżona - gubię się.
Jeśli chodzi o parsowanie to kompletnie nie wiem jak podejść do problemu. Aby nie zaśmiecać forum pokażę tylko kawałki kodu:

  1. <?php
  2. private function rpc_type($a) {
  3. if((string)$a === "true" || (string)$a === "false")return "boolean";
  4. if(is_int($a))return "int";
  5. if(is_double($a))return "double";
  6. if($a == (string)(int)$a){
  7. //if(substr($a,-2)==".0")return "double";
  8. if(substr($a,-1)!=".")return "int";
  9. }
  10. if("$a" === ("".(double)$a))return "double";
  11. if(base64_decode($a, true) !== false && substr($a,-1)==="=")return "base64";
  12. if(preg_match("/^((?:(?:[1]{1}\\d{1}\\d{1}\\d{1})|(?:[2]{1}\\d{3}))(?:[0]?[1-9]|[1][012])(?:(?:[0-2]?\\d{1})|(?:[3][0,1]{1})))(?![\\d])(T)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)$/", $a) || preg_match("/^((?:(?:[1]{1}\\d{1}\\d{1}\\d{1})|(?:[2]{1}\\d{3}))(?:[0]?[1-9]|[1][012])(?:(?:[0-2]?\\d{1})|(?:[3][0,1]{1})))(?![\\d])(T)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)(\\+)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)$/", $a))return "dateTime.iso8601"; //Looks funny, yea? ;] Lets write better one! biggrin.gif
  13. return "string";
  14. }
  15.  
  16.  
  17. private function build_struct($struct) {
  18. //Sample array:
  19. //["foo"] => "bar"
  20. //["childrens"] => 3
  21. //["name"] => "value"
  22.  
  23. $ret = "<struct>\n";
  24. foreach($struct as $name => $value) {
  25. $type = $this->rpc_type($value);
  26. $ret .= " <member>\n";
  27. $ret .= " <name>$name</name>\n";
  28. $ret .= " <value><$type>$value</$type></value>\n";
  29. $ret .= " </member>\n";
  30. }
  31. $ret .= "</struct>\n";
  32.  
  33. return $ret;
  34. }
  35.  
  36. private function build_array($array) {
  37. //Sample array:
  38. //[0] => a
  39. //[1] => 1
  40. //[2] => YWK=
  41. //<array> elements do not have names, use <struct> if you want names
  42.  
  43. $ret = "<array>\n";
  44. $ret .= " <data>\n";
  45. foreach($array as $name => $value) {
  46. $type = $this->rpc_type($value);
  47. $ret .= " <value><$type>$value</$type></value>\n";
  48. }
  49. $ret .= " </data>\n";
  50. $ret .= "</array>\n";
  51.  
  52. return $ret;
  53. }
  54.  
  55.  
  56. function build_call($params) {
  57. /*Sample $params array:
  58.   array(2) {
  59.   ["method_name"] => "system.listMethods"
  60.   ["params"]=>
  61.   array(4) {
  62.   ["value_0"] => "abcd"
  63.   ["struct_0"]=>
  64.   array(2) {
  65.   ["test"] => "123"
  66.   ["test2"] => "lala ;]"
  67.   }
  68.   ["array_0"]=>
  69.   array(4) {
  70.   [0] => "a"
  71.   [1] => "b"
  72.   [2] => "1"
  73.   [3] => "2"
  74.   }
  75.   ["value_1"] => "19980717T14:08:55+1:00"
  76.   }
  77.   }
  78.   */
  79. if(empty($params["method_name"]))return NULL;
  80.  
  81. $ret = '<?xml version="1.0" ?>'."\n";
  82. $ret .= " <methodCall>\n";
  83. $ret .= " <methodName>".$params["method_name"]."</methodName>\n";
  84. $ret .= " <params>\n";
  85.  
  86. if(isset($params["params"])) {
  87. foreach($params["params"] as $param_block_name => $param_block_data) {
  88. $tmp = "";
  89. if(stripos($param_block_name, "value_") !== false) {
  90. $type = $this->rpc_type($param_block_data);
  91. $tmp = " <value><$type>$param_block_data</$type></value>\n";
  92.  
  93. } elseif(stripos($param_block_name, "struct_") !== false) {
  94. $tmp = " <value>".$this->build_struct($param_block_data)."</value>\n";
  95.  
  96. } elseif(stripos($param_block_name, "array_") !== false) {
  97. $tmp = " <value>".$this->build_array($param_block_data)."</value>\n";
  98. }
  99.  
  100. if(!empty($tmp)) { $ret .= " <param>\n$tmp </param>\n"; }
  101. }
  102. }
  103. $ret .= " </params>\n";
  104. $ret .= " </methodCall>\n";
  105.  
  106. return $ret;
  107. }
  108. ?>


Wywołanie build_call z przykładową tablicą wygeneruje takie coś:
  1. <?xml version="1.0" ?>
  2. <methodCall>
  3. <methodName>system.listMethods</methodName>
  4. <params>
  5. <param>
  6. <value><string>abcd</string></value>
  7. </param>
  8. <param>
  9. <value><struct>
  10. <member>
  11. <name>test</name>
  12. <value><int>123</int></value>
  13. </member>
  14. <member>
  15. <name>test2</name>
  16. <value><string>lala ;]</string></value>
  17. </member>
  18. </struct>
  19. </value>
  20. </param>
  21. <param>
  22. <value><array>
  23. <data>
  24. <value><string>a</string></value>
  25. <value><string>b</string></value>
  26. <value><int>1</int></value>
  27. <value><int>2</int></value>
  28. </data>
  29. </array>
  30. </value>
  31. </param>
  32. <param>
  33. <value><dateTime.iso8601>19980717T14:08:55+1:00</dateTime.iso8601></value>
  34. </param>
  35. </params>
  36. </methodCall>


Wszytko było by ok gdyby nie to, że nie mogę zagnieżdżać dalej w drzewie, tzn nie mogę zrobić takiej tablicy:
Kod
    ["method_name"] => "system.listMethods"
    ["params"]=>
                array(4) {
                          ["value_0"] => "abcd"
                          ["struct_0"]=>
                                         array(2) {
                                                   ["test"] =>
                                                           array(2) {
                                                                 ["struct_0"] =>
                                                                    ["a"] => "b",
                                                                    ["b"] => "c"        
                                                                   }

                                                                 ["struct_1"] =>
                                                                    ["foo"] => "bar",
                                                                    ["ciocia"] => "ala"        
                                                                   }                                                                  
                                                   ["test2"] => "lala;]"
                                                  }
                         }


Wiem, że trzeba to jakoś parsować wywołując funkcję samą w sobie a nie zagnieżdżać fory+if = niestety nie wiem jak.

================================================================================
==========
Osobny problem: parsowanie wyniku. Ten sam problem co wcześniej - zagnieżdżenia party.gif
W odpowiedzi na zapytanie dostaje takiego XML`a:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <methodResponse>
  3. <params>
  4. <param><value><array><data>
  5. <value><array><data>
  6. <value><string>8BE80CE2312C1758C6BEE67374E088858630A354</string></value>
  7. <value><string>Mega.Ubuntu.Pack</string></value>
  8. <value><i8>1</i8></value>
  9. <value><i8>7150480939</i8></value>
  10. <value><i8>709541888</i8></value>
  11. <value><i8>0</i8></value>
  12. <value><i8>707788800</i8></value>
  13. <value><string>leech</string></value>
  14. <value><i8>1280158620</i8></value>
  15. <value><i8>21927</i8></value>
  16. <value><i8>558019627</i8></value>
  17. <value><i8>0</i8></value>
  18. <value><i8>1</i8></value>
  19. <value><i8>0</i8></value>
  20. <value><i4>0</i4></value>
  21. </data></array></value>
  22. <value><array><data>
  23. <value><string>CDC844A364AE2417EA204EA4840129B2298E7DD4</string></value>
  24. <value><string>ubuntu-10.04.1-desktop-i386.iso</string></value>
  25. <value><i8>1</i8></value>
  26. <value><i8>718864384</i8></value>
  27. <value><i8>158154752</i8></value>
  28. <value><i8>0</i8></value>
  29. <value><i8>155189248</i8></value>
  30. <value><string>leech</string></value>
  31. <value><i8>1282089174</i8></value>
  32. <value><i8>29825</i8></value>
  33. <value><i8>159052692</i8></value>
  34. <value><i8>18</i8></value>
  35. <value><i8>1</i8></value>
  36. <value><i8>2939379</i8></value>
  37. <value><i4>0</i4></value>
  38. </data></array></value>
  39. </data></array></value></param>
  40. </params>
  41. </methodResponse>


I kompletnie nie wiem jak do niego podejść. Dodatkowym ogranicznikiem jest dla mnie standaryzacja wyniku - chciałbym aby wygląd tablicy która zostanie zwrócona pasował do schematu z pierwszego postu.
Oczywiście schemat nie jest z góry ustalony - to jak to zrobiłem w funkcji generującej XML to jedyne co przyszło mi sensownie do głowy jeśli chodzi o rzutowanie struktury XML na tablicę wielowymiarową.


Czy ktoś dobroduszny mógłby pomóc w tym zagadnieniu? smile.gif
Crozin
1. Dlaczego typu wartości (string, boolean, int) nie podasz jako atrybut dla VALUE, tylko śmiecisz jakimiś dodatkowymi elementami?
2. Generowanie XMLa jako zlepek tekstów to średni pomysł. Masz takie narzędzia jak DOMDocument, więc skorzystaj z nich.
3. Nazwa elementu nie może zawierać kropki (patrz: dateTime.iso8601)
4. Nie mam pojęcia po co w ogóle chcesz jakoś formatować zwracany tekst (wcięcia, nowe linie). Mimo wszystko wspomniany DOMDocument załatwia sprawę, zapewniając formatowanie źródła dokumentu.

Popraw powyższe, a rozwiązanie problemu prawdopodobnie znajdzie się samo.

Swoją drogą, używanie pojedynczej spacji jako wcięcia to już kompletne zło. Używaj TABa i jeżeli lubisz tak niewyraźne wcięcia ustaw sobie w edytorze by znak ten był wyświetlany jak jedna-dwie spacje.
kiler129
Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
1. Dlaczego typu wartości (string, boolean, int) nie podasz jako atrybut dla VALUE, tylko śmiecisz jakimiś dodatkowymi elementami?

Powiedzmy, że jestem leniwy i nie chce o to dbać w przyszłości winksmiley.jpg Chcę aby sam skrypt ocenił co to jest winksmiley.jpg

Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
2. Generowanie XMLa jako zlepek tekstów to średni pomysł. Masz takie narzędzia jak DOMDocument, więc skorzystaj z nich.

Niestety wymagają PHP5.

Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
3. Nazwa elementu nie może zawierać kropki (patrz: dateTime.iso8601)

Jaknajbardziej może - http://www.xmlrpc.com/spec
Gdzie zapisano, że nie? Stosowałem już takie w XMLu i działały poprawnie.

Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
4. Nie mam pojęcia po co w ogóle chcesz jakoś formatować zwracany tekst (wcięcia, nowe linie). Mimo wszystko wspomniany DOMDocument załatwia sprawę, zapewniając formatowanie źródła dokumentu.

W sumie tylko dla estetyki i czytelności gdyby zaistniałą potrzeba debuggowania. Jeśli to zbyt trudne do wdrożenia to trudno, będzie bez.

Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
Popraw powyższe, a rozwiązanie problemu prawdopodobnie znajdzie się samo.

Siedzę nad tym od dłuższego czasu i nic.

Cytat(Crozin @ 25.09.2010, 19:22:22 ) *
Swoją drogą, używanie pojedynczej spacji jako wcięcia to już kompletne zło. Używaj TABa i jeżeli lubisz tak niewyraźne wcięcia ustaw sobie w edytorze by znak ten był wyświetlany jak jedna-dwie spacje.

Właśnie w tym problem, że nie mogę znaleźć tej opcji w moim edytorze.
p.s. Co masz na myśli mówiąc o czytelności wcięć? Na pewno czytelne nie jest wcięcie na 300px.


Pozdrawiam.
Crozin
Cytat
Powiedzmy, że jestem leniwy i nie chce o to dbać w przyszłości Chcę aby sam skrypt ocenił co to jest
Patrząc po Twoich przykładach nie robi tego sam. Chodzi o zamianę:
Kod
<value><int>123</int></value>
Na
Kod
<value type="int">123</value>
Co jest dużo bardziej sensownym, poprawnym, a co najważniejsze łatwym w użyciu rozwiązaniem.
Cytat
Niestety wymagają PHP5.
Co z tego? Z tego co widzę, Twój kod też wymaga PHP5.
Cytat
W sumie tylko dla estetyki i czytelności gdyby zaistniałą potrzeba debuggowania.
Przecież każde sensowne narzędzie do debuggowania XMLa (ot, nawet przeglądarka internetowa) Ci go sformatuje.
Cytat
Właśnie w tym problem, że nie mogę znaleźć tej opcji w moim edytorze.
Być może to pora na zmianę edytora w takim razie?
Cytat
p.s. Co masz na myśli mówiąc o czytelności wcięć? Na pewno czytelne nie jest wcięcie na 300px.
Oczywiście, że nie, ale takie na 5 pikseli również. Używaj TABa - ja sobie wyświetlę jako 4 spacje, Ty jako jedną.
kiler129
Jeśli chodzi o sposób zapisu typu danych nie mogę w niego ingerować - tak wymaga specyfikacja XML-RPC.
W którym miejscu mój kod wymaga PHP5? Jeśli chodzi o strict w b64 to mogę to szybko wyłączyć. Na razie faktycznie pracuję na piątce ale docelowo kod musi działać na 4 ... niestety.
Co do formatowania XML - masz rację przyznaję.

Cytat
Być może to pora na zmianę edytora w takim razie?

Łatwiej powiedzieć - trudniej zrobić. Gdy używałem Windows moim edytorem był Editpad Pro - gdy przesiadłem się na Macintosha zakupiłem Code (http://www.panic.com/coda/). Muszę głębiej się wczytać w ustawienia.
W każdym razie postaram się używać tabów winksmiley.jpg

EDIT:
Dla potomnych:

  1. <?php
  2. private function rpc_type($a) { //Detect type
  3. if((string)$a === "true" || (string)$a === "false")return "boolean";
  4. if(is_int($a))return "int";
  5. if(is_double($a))return "double";
  6. if($a == (string)(int)$a){
  7. if(substr($a,-2)==".0")return "double";
  8. if(substr($a,-1)!=".")return "int";
  9. }
  10. if("$a" === ("".(double)$a))return "double";
  11. if(base64_decode($a, true) !== false && substr($a,-1)==="=")return "base64";
  12. if(preg_match("/^((?:(?:[1]{1}\\d{1}\\d{1}\\d{1})|(?:[2]{1}\\d{3}))(?:[0]?[1-9]|[1][012])(?:(?:[0-2]?\\d{1})|(?:[3][0,1]{1})))(?![\\d])(T)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)$/", $a) || preg_match("/^((?:(?:[1]{1}\\d{1}\\d{1}\\d{1})|(?:[2]{1}\\d{3}))(?:[0]?[1-9]|[1][012])(?:(?:[0-2]?\\d{1})|(?:[3][0,1]{1})))(?![\\d])(T)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)(\\+)((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)$/", $a))return "dateTime.iso8601"; //Looks funny, yea? ;] Lets write better one! biggrin.gif
  13. return "string";
  14. }
  15.  
  16.  
  17. private function build_xml_array($data) { //Builds xml "array" element
  18. $tmp = "<data>\r\n";
  19. foreach($data as $key => $val) {
  20. $tmp.="<value>".$this->build_xml_core($val)."</value>\r\n";
  21. }
  22.  
  23. return $tmp."</data>\r\n";
  24. }
  25.  
  26.  
  27. private function build_xml_struct($data){
  28. $tmp = "";
  29. foreach($data as $key => $val) {
  30. $tmp.="<member>\r\n";
  31. $tmp.="<name>".$key."</name>\r\n";
  32. $tmp.="<value>".$this->build_xml_core($val)."</value>\r\n";
  33. $tmp.="</member>\r\n";
  34. }
  35.  
  36. return $tmp;
  37. }
  38.  
  39.  
  40. private function build_xml_params($data) { //Build xml "params" element
  41. $tmp = "";
  42. foreach($data as $key => $val) {
  43. $tmp.="<param>\r\n".
  44. "<value>".$this->build_xml_core(array($key=>$val))."</value>\r\n".
  45. "</param>\r\n";
  46. }
  47.  
  48. return $tmp;
  49. }
  50.  
  51.  
  52. private function build_xml_core($data){
  53. $tmp = "";
  54. if(is_array($data)) {
  55. foreach($data as $key => $val) {
  56. switch($key){
  57. case 'method_name':$key="methodName";break;
  58. default:
  59. if(($odl=strpos($key,"_"))!==false)
  60. $key=substr($key,0,$odl);
  61. }
  62.  
  63. if($key=="array")
  64. $tmp.="<".$key.">\r\n".$this->build_xml_array($val)."</".$key.">\r\n";
  65. elseif($key=="struct")
  66. $tmp.="<".$key.">\r\n".$this->build_xml_struct($val)."</".$key.">\r\n";
  67. elseif($key=="params")
  68. $tmp.="<".$key.">\r\n".$this->build_xml_params($val)."</".$key.">\r\n";
  69. elseif($key=="value")
  70. $tmp.=$this->build_xml_core($val);
  71. else $tmp.="<".$key.">".$this->build_xml_core($val)."</".$key.">\r\n";
  72. }
  73.  
  74. return $tmp;
  75. } else {
  76. $tag = $this->rpc_type($data);
  77. if($tag=="string")return $data; //string is defualt type so dont include it
  78. return "<".$tag.">".$data."</".$tag.">";
  79. return $data;
  80. }
  81. }
  82.  
  83.  
  84. function make_human_xml($data) { //disbaled by default => performance
  85. $how=0;
  86. $data2=explode("\n",$data);
  87. $ile=count($data2);
  88. for($i=0;$i<$ile;$i++){
  89. $stan = substr_count($data2[$i],"<")-(2*substr_count($data2[$i],"</"));
  90. if($stan<0)$how+=$stan;
  91. $data2[$i] = str_repeat("\t",$how).$data2[$i];
  92. if($stan>0)$how+=$stan;
  93. }
  94.  
  95. return join("\n",$data2);
  96. }
  97.  
  98.  
  99. function build_xml_call($data, $human_xml=false) {
  100. /*Sample $params array:
  101.   array(2) {
  102.   ["method_name"] => "system.listMethods"
  103.   ["params"]=>
  104.   array(4) {
  105.   ["value_0"] => "pussy"
  106.   ["struct_0"]=>
  107.   array(2) {
  108.   ["test"] => "123"
  109.   ["test2"] => "lala ;]"
  110.   }
  111.   ["array_0"]=>
  112.   array(4) {
  113.   [0] => "a"
  114.   [1] => "b"
  115.   [2] => "1"
  116.   [3] => "2"
  117.   }
  118.   ["value_1"] => "19980717T14:08:55+1:00"
  119.   }
  120.   }
  121.   */
  122. if(empty($data["method_name"]))return NULL;
  123.  
  124. $ret = '<?xml version="1.0" ?>'."\r\n";
  125. $ret .= "<methodCall>\r\n";
  126. $ret .= $this->build_xml_core($data);
  127. $ret .= " </methodCall>\r\n";
  128.  
  129. if($human_xml) {
  130. return $this->make_human_xml($ret);
  131. } else {
  132. return $ret;
  133. }
  134. }
  135. ?>


Teraz czy ktoś ma pomysł na parsowanie otrzymanych danych?

/kolorafa - może ty? ;]/
zegarek84
Łałć ;/ .. tak cieżko w dokumentacji sprawdzić co zadziała na php4?? luknij na Przetwarzanie XML i nizej na DOM XML (PHP 4) - poza tym w manualu jest więcej tego - ale ja ze swojej strony polecam funkcje z rodziny DOM
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.