Skip to content Skip to sidebar Skip to footer

Deserialize Xml Elements To Different Types, Based On Attribute

I have an XML document with many Entity elements, which each have an attribute of either type='foo' or type='bar'. See this sample: &l

Solution 1:

Now the real problem ...

// TODO: What annotations to put here?privateList<BarEntity> barEntities;

My answer: none! Or at least, it doesn't matter!

Attributes like type here are just strings and can't make any decisions. But there's another nice way:

  1. Implement a Converter for RootNode which does the decision
  2. Use a Serializer to do the actual work of deserializing each entity.

I made some modifications to your classes, but nothing spectacular has been changed. The toString()-method is for testing only - implement as you need it.

Class FooEntity

@Root(name = "Entity")
publicclassFooEntity
{
    @Attribute(name = "type")
    privateStringtype;
    @Element(name = "Price")
    private int price;

    /*
     * NOTE: A default ctor is required - visibile doesn't matter
     */@OverridepublicStringtoString()
    {
        return"FooEntity{" + "price=" + price + '}';
    }
}

Class BarEntity

@Root(name = "Entity")
publicclassBarEntity
{
    @Attribute(name = "type")
    privateStringtype;
    @Element(name = "URL")
    privateString url;

    /*
     * NOTE: A default ctor is required - visibile doesn't matter
     */@OverridepublicStringtoString()
    {
        return"BarEntity{" + "url=" + url + '}';
    } 
}

Class RootNode

@Root(name = "RootNode")
@Convert(RootNodeConverter.class)   // <--- Important!classRootNode
{
    privateList<FooEntity> fooEntities;
    privateList<BarEntity> barEntities;


    publicRootNode()
    {
        // This has to be done somewhere ...this.fooEntities = newArrayList<>();
        this.barEntities = newArrayList<>();
    }


    publicList<FooEntity> getFooEntities()
    {
        return fooEntities;
    }

    publicList<BarEntity> getBarEntities()
    {
        return barEntities;
    }

    @OverridepublicStringtoString()
    {
        return"RootNode{" + "fooEntities=" + fooEntities + ", barEntities=" + barEntities + '}';
    }
}

And finally the Converter-implementation:

Class RootNodeConverter

publicclassRootNodeConverterimplementsConverter<RootNode>
{
    @Overridepublic RootNode read(InputNode node)throws Exception
    {
        RootNoderoot=newRootNode();
        finalInputNodeentities= node.getNext("Entities");
        InputNode child;

        while( ( child = entities.getNext() ) != null )
        {
            if( child.getName().equals("Entity") == false )
            {
                continue; //  Not an Entity
            }

            finalSerializerser=newPersister();

            switch(child.getAttribute("type").getValue())
            {
                case"foo":
                    root.getFooEntities().add(ser.read(FooEntity.class, child));
                    break;
                case"bar":
                    root.getBarEntities().add(ser.read(BarEntity.class, child));
                    break;
                default:
                    // Not a Foo nor a Bar - what now!?break;
            }
        }

        return root;
    }


    @Overridepublicvoidwrite(OutputNode node, RootNode value)throws Exception
    {
        thrownewUnsupportedOperationException("Not implemented yet!");
    }
}

There are some things to optimize, eg. add a root.addBar(ser.read(BarEntity.class, child)) or errorhandling in general.

Btw. instead of two lists, you can maintain a single one (if relevant). Just make a superclass for the entities. You can move the type-attribute to there too.

Usage

Here's an example how to use:

finalString input ="<RootNode>\n"+"    <Entities>\n"+"        <Entity type=\"foo\">\n"+"            <Price>1</Price>\n"+"        </Entity>\n"+"\n"+"        <Entity type=\"bar\">\n"+"            <URL>www.google.co.uk</URL>\n"+"        </Entity>\n"+"\n"+"        <Entity type=\"foo\">\n"+"            <Price>77</Price>\n"+"        </Entity>\n"+"    </Entities>\n"+"</RootNode>";


finalSerializer ser = new Persister(new AnnotationStrategy()); // <-- Note the strategy!RootNode root = ser.read(RootNode.class, input);
System.out.println(root);

Nothing really spectacular here too ...

Output:

RootNode{fooEntities=[FooEntity{price=1}, FooEntity{price=77}], barEntities=[BarEntity{url=www.google.co.uk}]}

Post a Comment for "Deserialize Xml Elements To Different Types, Based On Attribute"