1

I am trying to read the keys and values in an XML file which I first created as an object and then tried to iterate in a foreach loop. My XML file:

<?xml version="1.0" encoding="UTF-8"?>
<response>
<lst name="responseHeader"><int name="status">0</int><int name="QTime">10</int><lst name="params"><str name="q">*:*</str><str name="facet.field">main</str><str name="facet.mincount">1</str><str name="rows">0</str><str name="facet">on</str></lst></lst><result name="response" numFound="5473" start="0"/><lst name="facet_counts"><lst name="facet_queries"/><lst name="facet_fields"><lst name="main"><int name="Victoria University Photograph Collection">1693</int>

......

My php code is:

foreach($input as $int){
    echo $int->response->int;
    $arr = array();

   foreach ($int as $key => $value){
        $arr[(string)$key]=(string)$value;
   }
   $arr[]=$arr;
}
print_r($arr);

The echo line does not print anything and the array only prints the xml content that does not have any value. So it only prints [lst]. I need to retrieve the name and value within the tag.

Thank you.

2
  • How are you turning your XML document into $input? Can you add that code to your question? Commented Oct 5, 2018 at 21:47
  • $input = simplexml_load_file('collections.xml'); Commented Oct 5, 2018 at 21:56

3 Answers 3

1

You could use an xpath expression to get the name and the value of the int elements inside the lst element.

If you want to put those in an array using the name as the key you might use a multidimensional array where the key value is in it's own array because when the names are the same in the xml the keys using a single dimensional array will be overwritten.

$items = $input->xpath('/response/lst/int');
$arr = [];
foreach ($items as $item) {
    if (isset($item->attributes()->name) && trim((string)$item) !== "") {
        $arr[] = [(string)$item->attributes()->name => (string)$item];
    }
}
print_r($arr);

Demo

Or to get all the names and values inside the int tag:

$items = $input->xpath('/response/lst//*');
$arr = [];
foreach ($items as $item) {
    if (isset($item->attributes()->name) && trim((string)$item) !== "") {
        $arr[] = [(string)$item->attributes()->name => (string)$item];
    }
}
print_r($arr);

Demo

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! This works. However, I am having trouble formatting the result. The result looks like: 'Array ( [0] => Array ( [0] => Array ( [status] => 0 ) ) [1] => Array ( [0] => Array ( [QTime] => 10 ) )' - And I need the result to be stored in one Array ("status" => 0 .... etc).
@Krutheeka That is what I mentioned in my answer. I have done it this way because if you have multiple status keys they will be overwritten. So "status" => 0 will become "status" => 1 etc..
I had to change my conversion from xml to php as: $input=file_get_contents('collections.xml'); How would i parse through the data now?
0

Read the PHP manual for SimpleXML: http://php.net/manual/en/book.simplexml.php

Also, you XML is poorly formatted, you are using the same element name for different values. The element name should specify what the value is.

1 Comment

This is the XML file that was assigned to me, I unfortunately don't have the power to change the content of the XML file.
0

I've reformatted your XML for readability (the content is unmodified). It looks like you only posted a part of your XML in your question, since you're missing a few closing </lst> tags at the end and you're missing a closing </response> as well:

$xml = '
    <?xml version="1.0" encoding="UTF-8"?>
    <response>
        <lst name="responseHeader">
            <int name="status">0</int>
            <int name="QTime">10</int>
            <lst name="params">
                <str name="q">*:*</str>
                <str name="facet.field">main</str>
                <str name="facet.mincount">1</str>
                <str name="rows">0</str>
                <str name="facet">on</str>
            </lst>
        </lst>
        <result name="response" numFound="5473" start="0"/>
        <lst name="facet_counts">
            <lst name="facet_queries"/>
            <lst name="facet_fields">
                <lst name="main">
                    <int name="Victoria University Photograph Collection">1693</int>
                </lst>' /* added by me */ . '
            </lst>' /* added by me */ . '
        </lst>' /* added by me */ . '
    </response>' /* added by me */ . '
';

$input = simplexml_load_string($xml);

SimpleXML will return a reference to the root element, in your case <response>. As you can see, in your example code this <response> element contains two <lst> children and one <result> child. This means $input->lst will be an array containing two entries: the two <lst> elements in the order in which they appear in your document.

Your code is specifically looking for <int> elements as a first child of something, which only appears to be the case for the first <lst> element. There's another one in the second <lst> element, but that one's hidden away at a depper level so I'll skip it for now. So basically, we need to look for <int>s in $input->lst[0].

$arr = array();
foreach ($input->lst[0]->int as $int) {
    // get the name from the attributes
    $name = (string)$int['name'];

    // get the element's text content
    $value = (string)$int;

    $arr[$name] = $value;
}

print_r($arr);

If you want to search through all (top-level) <lst> you will need an extra loop:

$arr = array();
foreach ($input->lst as $lst) {
    foreach ($lst->int as $int) {
        $name = (string)$int['name'];
        $value = (string)$int;

        $arr[$name] = $value;
    }
}

print_r($arr);

With your example code, both cases output the following array (since the <int> in the second <lst> is buried a little deeper):

Array
(
    [status] => 0
    [QTime] => 10
)

Now, if you want to look through any nested <lst> no matter how deep, you might want to consider making a recursive function out of it:

function parseLst($lst, &$arr) {
    // note the & before $arr, this is necessary to make `$arr[$name] = $value;` work.
    // Another option would be to use `global $arr;` and not pass it as an argument,
    // or if you're doing this in a class, to use `$this->arr` instead.

    foreach ($lst->int as $int) {
        $name = (string)$int['name'];
        $value = (string)$int;

        $arr[$name] = $value;
    }

    foreach ($lst->lst as $childLst) {
        parseLst($childLst, $arr);
    }
};

$arr = array();

foreach ($input->lst as $lst) {
    parseLst($lst, $arr);
}

print_r($arr);

This will yield:

Array
(
    [status] => 0
    [QTime] => 10
    [Victoria University Photograph Collection] => 1693
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.