On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • Hi there,

    this is the official support channel for ParseX for MODX revolution. As you found it, I assume you have an issue with it.

    Link to MODX repository: http://modx.com/extras/package/parsex
    Current Version: 1.0.1-pl
    Works with: any version of Revolution, I guess

    What does ParseX do?
    It reads a rss feed or other xml file and puts its contents into MODX placeholders so you can use them in your templatesor chunks.

    Hot do I get help?
    Please let me know what you are trying to do, how you noticed that it doesnt work the way you want it to and what you have tried until this moment to solve it.

    Cheers!
    • Hi! First of all, I want to say that ParseX a great component for me.

      But there are a couple of questions and suggestions:

      How can i get data from the second level of nesting?
      <item>
       <level_1>LEVEL1</level_1>
        <level_2>
          <child_level_2>LEVEL2</child_level_2>
        </level_2>
       </item>
      

      With first level everything cool, just [[+Level1]], and which placeholder use for child of the second level? [[+child_level_2]] not working sad

      In my case, the source has a strange structure:
      <item>
      <item>
       <level_1>LEVEL1</level_1>
        <level_2>
          <child_level_2>LEVEL2</child_level_2>
        </level_2>
       </item>
      <item>
       <level_1>LEVEL1</level_1>
        <level_2>
          <child_level_2>LEVEL2</child_level_2>
        </level_2>
       </item>
      </item>
      

      The main container <item> is the same as his childrens <item>! In the end the first result is empty. Maybe you could add something like "Skip the results from the beginning" as the &offset = `1`

      Debugmode:
      object(SimpleXMLElement)#33 (1) {
        ["item"]=>
        array(2) {
          [0]=>
          object(SimpleXMLElement)#26 (2) {
            ["level_1"]=>
            string(6) "LEVEL1"
            ["level_2"]=>
            object(SimpleXMLElement)#28 (1) {
              ["child_level_2"]=>
              string(6) "LEVEL2"
            }
          }
          [1]=>
          object(SimpleXMLElement)#27 (2) {
            ["level_1"]=>
            string(6) "LEVEL1"
            ["level_2"]=>
            object(SimpleXMLElement)#28 (1) {
              ["child_level_2"]=>
              string(6) "LEVEL2"
            }
          }
        }
      }
      array(1) {
        ["item"]=>
        string(7) "
       
        
       "
      }
      


      Thank you in advance! [ed. note: ryaboy_ua last edited this post 3 years, 7 months ago.]
      • Hi Alexey,

        I dont have a lot of time right now. But I looked into the code. 2nd level items: cant do it yet. But i updated the code for "offset" it's quick and dirty. Can you copy / paste it yourself into the snippet?
        Just replace the existing code inside the snippet.

        <?php
        # Snippet to read and parse XML input
        # USAGE: [[!parsex? &source=`feed.rss` &tpl=`xmlTpl`]]
        # author: guido.gallenkamp@bytethinker.com
        # Let me know if you add or change things, maybe I can add them to the package in a later version!
        
        //$modx->setDebug(true);
         
        $source = $modx->getOption('source', $scriptProperties, 'http://modx.com/feeds/latest.rss');
        $element = $modx->getOption('element', $scriptProperties, 'item');
        $tpl = $modx->getOption('tpl', $scriptProperties, 'xmlTpl');
        $wrapper = $modx->getOption('wrapper', $scriptProperties, 'wrapX');
        $limit = $modx->getOption('limit', $scriptProperties, 0);
        $offset = $modx->getOption('offset', $scriptProperties, 0);
        $debugmode = $modx->getOption('debugmode', $scriptProperties, false);
        $dataformat = $modx->getOption('dataformat', $scriptProperties, 'xml');
        
        /*echo $dataformat;
        break; */
        
        
        if (empty($source)) {
            $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] Empty source adress passed, aborting.');
            return 'No source definded.';
        }
        
        else {
            if ($dataformat == 'json') {
                if (!$json = file_get_contents($source)) {
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] can NOT read JSON file: '.$source);
                    }
                if (!$obj = json_decode($json, true)) {
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] cant decode JSON file: '.$source);
                    if (json_last_error() != JSON_ERROR_NONE) {
                        $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] JSON error: '.json_last_error_msg());
                    }
                }
                // creating object of SimpleXMLElement
                $xmln = new SimpleXMLElement("<?xml version=\"1.0\"?><container></container>");
                array_to_xml($obj,$xmln);
                
                $newxml = true;
            }
            if (($dataformat == 'xml')||($newxml==true)) {
                if ($newxml) {
                    $xmldata = $xmln;
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] new XML object from JSON data ');
                    //var_dump($xmldata);
                }
                else {
                    $xmldata = file_get_contents($source);
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] XML data directly from remote source ');
                }
                if ($xml = simplexml_load_string($xmldata)) {
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] can read file: '.$source);
                    $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] element value '.$element);
                    // find parent element
                    // select parent element
                    // iterate children of parent element to receive elements containers attributes, too
                    
                    if (!$nodes = $xml->xpath("//$element")) {
                        $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] no nodes ');
                    }
            
                    $nodecount = 0;
                //echo 'Nodes: '.count($nodes).'<br />';
                //echo $nodes->count();
                    foreach ($nodes as $node)
                    {
                        if ($offset > $nodecount) {
                            $nodecount++;
                            //$modx->log(modX::LOG_LEVEL_ERROR,'[parseX] '.$offset.' is greater than '.$nodecount );
                            continue;
                        }
                        else {
        
                        if (($debugmode==true) && ($nodecount==0)) {
                            //var_dump($node);
                        }
                        $values = array();
                        $attrib = $node->attributes();
             
                        foreach ($attrib as $attrkey => $attrval) {
                          $values['.'.$attrkey] = (string)$attrval;  
                        }
                        // $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($node));
            
                        foreach($node as $key => $value)
                        {
                            if ($key == 'pubDate') {
                                    $value = strftime("%d.%m.%Y %H:%M:%S", strtotime($value));
                                }
                            $values[$key] = (string)$value;
            
                            foreach ($node->$key->attributes() as $attrkey => $attrval) {
                                $values[$key.'.'.$attrkey] = (string)$attrval;
                            }
                        }
                        if ($debugmode==true && $nodecount==0) {
                         //   var_dump($values);
                        }
                    $output .= $modx->getChunk($tpl, $values);
                    $nodecount++;
                    if (($nodecount >= $limit) && ($limit !=0)) break;
                    }
                    }
            
                }
            }
            else {
                $modx->log(modX::LOG_LEVEL_ERROR,'[parseX] can NOT read file: '.$source);
            }
        
        $result = array("result" => $output);
        return $modx->getChunk($wrapper, $result);
        }
        
        function array_to_xml($obj, &$xmln) {
            foreach($obj as $key => $value) {
                if(is_array($value)) {
                    if(!is_numeric($key)){
                        $subnode = $xmln->addChild("$key");
                        array_to_xml($value, $subnode);
                    }
                    else{
                        $subnode = $xmln->addChild("item$key");
                        array_to_xml($value, $subnode);
                    }
                }
                else {
                    $xmln->addChild("$key",htmlspecialchars("$value"));
                }
            }
        }


        I have more time next week, sorry!

        Guido
        • Hi gallenkamp,

          I just tried ParseX and got it working just fine with an external test RSS.

          What I want to do now I'm unsure how to do with ParseX.

          I have a list of 1200+ lines of text (all birthdays in different years) in an xml (rss) that I want to filter down to historical dates.

          For example if there are 5 birthdays on 12th August but in different years I want to call only these 5 lines of text.

          Then on 13th August if there only 4 birthdays I only want the webpage to display only these 4 lines of text.

          On every different day of the year the page will only display the data aligned with each day.

          Is it possible to do this with ParseX?

          If yes, how?
          • You might be better off pulling the data into a custom database table, unless it's constantly being updated.
              Did I help you? Buy me a beer
              Get my Book: MODX:The Official Guide
              MODX info for everyone: http://bobsguides.com/modx.html
              My MODX Extras
              Bob's Guides is now hosted at A2 MODX Hosting
            • Quote from: BobRay at Apr 08, 2016, 03:43 AM
              You might be better off pulling the data into a custom database table, unless it's constantly being updated.

              Hi Bob,

              I could do that, but how would I call and display only the required data?
              The part I can't get my head around is how to differentiate all the dates and months that appear in different years.
              • If you set up the table and class for xPDO, you can use its query language to search for what you want.

                If you import the data, you could store the month, date, and year in separate fields. Then a query for a particular month and date, regardless of year, would be very easy.

                If you have the option to get (or convert) the incoming data as JSON or CSV, it might make the import much easier.
                  Did I help you? Buy me a beer
                  Get my Book: MODX:The Official Guide
                  MODX info for everyone: http://bobsguides.com/modx.html
                  My MODX Extras
                  Bob's Guides is now hosted at A2 MODX Hosting
                • Quote from: BobRay at Apr 08, 2016, 07:25 PM
                  If you set up the table and class for xPDO, you can use its query language to search for what you want.

                  If you import the data, you could store the month, date, and year in separate fields. Then a query for a particular month and date, regardless of year, would be very easy.

                  If you have the option to get (or convert) the incoming data as JSON or CSV, it might make the import much easier.

                  Hi Bob,

                  I've created a table using the following code:

                  SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
                  CREATE TABLE IF NOT EXISTS `birthday_quotation` (
                  `id` int(25) unsigned NOT NULL auto_increment,
                  `quote` mediumtext NOT NULL,
                  `author` varchar(200) NOT NULL default '',
                  `topic` varchar(20) NOT NULL default '',
                  `birthday` varchar(20) NOT NULL default '',
                  `month` varchar(20) NOT NULL default '',
                  `date` varchar(20) NOT NULL default '',
                  PRIMARY KEY (`id`),
                  KEY `topic` (`topic`),
                  KEY `author` (`author`),
                  KEY `month` (`month`),
                  KEY `date` (`date`),
                  KEY `birthday` (`birthday`),
                  FULLTEXT KEY `quote` (`quote`,`author`,`topic`,`birthday`)
                  ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;


                  So far I've populated it with data for 9th April, 10th April & 11th April only. (for testing, i want to populate it for 365 days if can work).

                  What code do I need to use to display the following?:

                  Quote
                  Author
                  Topic
                  Birthday

                  The list must be in that order.

                  fyi......
                  The value for 'month' is April
                  The value for 'date' is 9, 10 or 11
                  The value for 'birthday' is 9th April 1972 or 9th April 1951 or 10th April 1932.....etc

                  I kind of figured that an easy way to display the required data for every current date would be a query that must include April for month and 9 for date if current date is 9th April.....a query that must include April for month and 10 for date if current date is 10th April.......and so on for every month and current daily date.
                  • Hi Bob,

                    I fail when try to use your zip code for CreateXpdoClasses.

                    After name the snippet CreateXpdoClasses i c&p the code in and after save I just see a blank screen.

                    I then changed the prefix to birthday_ and still the same blank screen.

                    I try to use this reverse engineering script also:

                    https://rtfm.modx.com/revolution/2.x/case-studies-and-tutorials/reverse-engineer-xpdo-classes-from-existing-database-table

                    This just fail also, I don't think I edit the fields correctly. [ed. note: jimmyjazz last edited this post 3 years, 2 months ago.]
                    • I try to create the xml schema by myself:

                      <model package="quotes" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
                      <object class="BirthdayQuotes" table="birthday_quotation" extends="xPDOSimpleObject">
                      <field key="id" dbtype="int" precision="25" phptype="integer" null="false" default=""/>
                      <field key="quote" dbtype="mediumtext" null="false" default=""/>
                      <field key="author" dbtype="varchar" precision="200" phptype="string" null="false" default=""/>
                      <field key="topic" dbtype="varchar" precision="20" phptype="string" null="true"/>
                      <field key="birthday" dbtype="varchar" precision="20" phptype="string" null="false" default=""/>
                      <field key="month" dbtype="varchar" precision="20" phptype="string" null="false" default=""/>
                      <field key="date" dbtype="varchar" precision="20" phptype="string" null="false" default=""/>
                      
                          <field key="publishedby" dbtype="int" precision="25" phptype="integer" null="false" default="0" />
                       
                          <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                              <column key="id" collation="A" null="false" />
                          </index>
                               
                              <aggregate alias="Resource" class="modResource" local="resource_id" foreign="id" cardinality="one" owner="foreign" />
                              <aggregate alias="Creator" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign" />
                      </object>
                      </model>


                      Would this be correct?

                      If no what do I need to adjust?

                      If it is correct, how do I test it or what do I do with it next?