Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse XML and store it in a Map with SimpleXML?

I'm having trouble parsing xml with simple xml framework. I want to store category id and list of tournaments inside a Map/hashMap how can I do that? I followed the tutorial on simple xml but it doesnt work for me.

I stored it in a list like this:

 @ElementList(entry = "Category", inline = true, required = false)
List<Category> category;

but now I wanna store it in a map.

Here is xml: enter image description here

Tutorial that I followed: enter image description here

Any help would be appreciated, tnx.

like image 294
Nenco Avatar asked Aug 23 '16 13:08

Nenco


2 Answers

It's not possible by Annoations like @ElementList or @ElementMap, but using a Converter is still an (good) option.

Going that way means: Implementing a Converter for Tournaments which merges the Category-Id / Tournament List pair.

And here is how this is done:


First the necessary (data-) classes.

Tournament:

@Root(name = "Tournament")
public class Tournament
{
    @Text
    private String data;

    public Tournament(String data)
    {
        this.data = data;
    }

    private Tournament() { /* Required */ }

    // ...
}

Note: I gave the Tournament some data string, since the real data isn't shown. However, actually it doesn't matter what data it contains.

Category:

@Root(name = "Category")
public class Category
{
    private String id;

    // ...
}

Tournaments

@Root(name = "Tournaments")
@Convert(TournamentsConverter.class) // Specify the Converter used for this class
public class Tournaments
{
    private Map<String, List<Tournament>> tournaments;

    public Tournaments()
    {
        tournaments = new HashMap<>();
    }


    protected void put(String id, List<Tournament> tournaments)
    {
        this.tournaments.put(id, tournaments);
    }

    // ...
}

The @Convert will use the following Converter for serialization / deserialization of Tournaments. I haven't implemented the serialization part in this example (it's not difficult though).

Important: @Convert requires an AnnotationStrategy (see example below)!

Tournament Converter

public class TournamentsConverter implements Converter<Tournaments>
{
    private final Serializer serializer = new Persister();


    @Override
    public Tournaments read(InputNode node) throws Exception
    {
        Tournaments tournaments = new Tournaments();
        InputNode childNode = node.getNext();

        // Iterate over all childs of 'Tournaments'
        while( childNode != null )
        {
            if( childNode.getName().equals("Category") == true )
            {
                final String categoryId = childNode.getAttribute("category_id").getValue();

                List<Tournament> tournamentList = new ArrayList<>();
                InputNode child = childNode.getNext();

                // Iterate over all childs of 'Category'
                while( child != null )
                {
                    // Use a Serializer to read the nodes data
                    Tournament tournament = serializer.read(Tournament.class, child);
                    tournamentList.add(tournament);

                    child = childNode.getNext();
                }

                // Insert the Id / Tournament's pair
                tournaments.put(categoryId, tournamentList);
            }

            childNode = node.getNext();
        }

        return tournaments;
    }


    @Override
    public void write(OutputNode node, Tournaments value) throws Exception
    {
        // Implement as needed
        throw new UnsupportedOperationException("Not supported yet.");
    }

}

All the "magic" is done by the Converter:

  1. Iterate over all of Tournaments child nodes
  2. If it's a Category
    1. Get the id
    2. Iterate over all childs and add the Tournament's to a list
    3. Add the id / List of Tournament to the result Tournaments

As shown above, it's possible to use a Serializer for (de-)serialization of nodes. No need to implement that by hand.

Example

Finally, here's an example. Please note the AnnotationStrategy.

Serializer ser = new Persister(new AnnotationStrategy());

final String xml = "<Tournaments>\n"
        + "   <Category category_id=\"289\">\n"
        + "      <Tournament>aaaa</Tournament>\n"
        + "   </Category>\n"
        + "   <Category category_id=\"32\">\n"
        + "      <Tournament>bbbd</Tournament>\n"
        + "      <Tournament>cccc</Tournament>\n"
        + "   </Category>\n"
        + "</Tournaments>";

Tournaments t = ser.read(Tournaments.class, xml);

System.out.println(t);

Output:
Using generated toString() added to each class.

Tournaments{tournaments={289=[Tournament{data=aaaa}], 32=[Tournament{data=bbbd}, Tournament{data=cccc}]}}
like image 139
ollo Avatar answered Oct 20 '22 00:10

ollo


The problem is that you are trying to create a structure like Map<String, <List<Tournament>> and that doesn't seem to be possible with the ElementMap annotation. You need sort of an ElementMapList annotation. Maybe you can file an issue and Mr. Gallagher can add that.

like image 22
kris larson Avatar answered Oct 20 '22 01:10

kris larson