Asked  7 Months ago    Answers:  5   Viewed   48 times

I'm trying to read an RSS feed from Flickr but it has some nodes which are not readable by Simple XML (media:thumbnail, flickr:profile, and so on).

How do I get round this? My head hurts when I look at the documentation for the DOM. So I'd like to avoid it as I don't want to learn.

I'm trying to get the thumbnail by the way.

 Answers

88

The solution is explained in this nice article. You need the children() method for accessing XML elements which contain a namespace. This code snippet is quoted from the article:

$feed = simplexml_load_file('http://www.sitepoint.com/recent.rdf'); 
foreach ($feed->item as $item) { 
    $ns_dc = $item->children('http://purl.org/dc/elements/1.1/'); 
    echo $ns_dc->date; 
}
Wednesday, March 31, 2021
 
Giovanni
answered 7 Months ago
28

From http://bugs.php.net/49660:

Since version 2.7.3 libxml limits the maximum size of a single text node to 10MB. The limit can be removed with a new option, XML_PARSE_HUGE. PHP has no way to specify this option to libxml.

So I imagine this flag is the way that PHP now has to specify this option.

Wednesday, March 31, 2021
 
muaaz
answered 7 Months ago
24

simplexml_load_string() (as the name suggest) load xml from a string and returns an object of SimepleXMLElement. There is no difference between this and using just the usual constructor of the class.

Update:

SimpleXML::__construct() has an additional parameter (the third one) bool $data_is_url = false. If this is true the behavior is the same as simplexml_load_file() (in conjunction with the common stream wrappers). Thus both simplexml_load_*()-functions cover the same functionality as SimpleXML::__construct().

Additional the functions simplexml_load_*() has a second parameter string $class_name = "SimpleXMLElement" to specify the class of the object to get returned. Thats not a specific feature of the functions, because you can "use" something very similar with usual instanciation too

class MyXML extends SimpleXMLElement {}
$a = new MyXML($xml);
$b = simplexml_load_string($xml, 'MyXML');

A difference between the OOP- and the procedural approach is, that the functions return false on error, but the constructor throws an Exception.

Saturday, May 29, 2021
 
rlanvin
answered 5 Months ago
47

If there no end tag - xml is not valid. If there only issue with none tag you can try ignore it in your parser. Or even try remove none tags with php "find and replace" functions like preg_replace before dealing with parser

Saturday, May 29, 2021
 
MGP
answered 5 Months ago
MGP
53

You can't use @Text annotation here, this is only possible if you don't have any child.

and it [@Text annotation] can not appear with the another XML element annotations, such as the Element annotation.

Source: @Text API documentation

However, you can use a Converter to those text. This is a bit tricky, but here's an example:

Criteria class:

@Root(name = "Criteria")
public class Criteria
{
    @Attribute(name = "Type")
    private String type;
    @Attribute(name = "Source")
    private String source;
    @ElementList(name = "Values", inline = true)
    private ArrayList<Value> values;



    public Criteria(String type, String source)
    {
        this.type = type;
        this.source = source;
        this.values = new ArrayList<>();
    }

    private Criteria() { }


    // ...


    @Override
    public String toString()
    {
        return "Criteria{" + "type=" + type + ", source=" + source + ", values=" + values + '}';
    }


    // Inner class for values - you also can use a normal one instead
    @Root(name = "Value")
    public static class Value
    {
        @Attribute(name = "Type", required = true)
        private int type;
        @Text(required = true)
        private double value;


        public Value(int type, double value)
        {
            this.type = type;
            this.value = value;
        }

        private Value() { }

    } 

}

Question class:

@Root(name = "Question")
@Convert( value = Question.QuestionConvert.class)
public class Question
{
    @Attribute(name = "ID", required = true)
    private String id;
    @Element(name = "text")
    private String text;
    @ElementList(inline = true)
    private ArrayList<Criteria> criteria;


    public Question(String id)
    {
        this.id = id;
        this.criteria = new ArrayList<>();

        this.text = "This inner text ...";
    }

    private Question() { }


    // ...


    @Override
    public String toString()
    {
        return "Question{" + "id=" + id + ", text=" + text + ", criteria=" + criteria + '}';
    }



    static class QuestionConvert implements Converter<Question>
    {
        private final Serializer ser = new Persister();


        @Override
        public Question read(InputNode node) throws Exception
        {
            Question q = new Question();
            q.id = node.getAttribute("ID").getValue();
            q.text = node.getValue();

            q.criteria = new ArrayList<>();
            InputNode criteria = node.getNext("Criteria");

            while( criteria != null )
            {
                q.criteria.add(ser.read(Criteria.class, criteria));
                criteria = node.getNext("Criteria");
            }

            return q;
        }


        @Override
        public void write(OutputNode node, Question value) throws Exception
        {
            node.setAttribute("ID", value.id);
            node.setValue(value.text);


            for( Criteria c : value.getCriteria() )
            {
                ser.write(c, node);
            }
        }
    }
}

Please note all those empty constructors. They are required by simple but you can keep them private. You don't have to implement those inner classes as inner.

The key to the solution is the Converter which allows you to use text and child-elements together. You can use a Serializer to write all the Criteria-childs.

There are some toString() methods, those are only for testing - you can implement them as you need.

Input XML:

<Question ID="Q1">This inner text ...
   <Criteria Type="Normal" Source="OEM">
      <Value Type="0">45.7</Value>
      <Value Type="100">42.7</Value>
   </Criteria>
   <Criteria Type="Impact" Source="OEM">
      <Value Type="0">45.7</Value>
      <Value Type="100">42.7</Value>
   </Criteria>
</Question>

Example code:

Serializer ser = new Persister(new AnnotationStrategy()); // Don't miss the AnnotationStrategy!

Question q = ser.read(Question.class, f);
System.out.println(q);

Output:

Question{id=Q1, text=This inner text ...
   , criteria=[Criteria{type=Normal, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}, Criteria{type=Impact, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}]}

Not very beautiful, but it's working! :-)

Ps. Since both methods of the Converter are implemented you also can use this code to serialize a Question object.

Friday, August 20, 2021
 
peixotorms
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :