Mapping XML elements and XML text to a Collection

63 views
Skip to first unread message

Richard Hague

unread,
Jan 12, 2010, 9:58:53 AM1/12/10
to xmappr
Hi,
I'm currently trying to map some xml in the following format:

<div><strong>Formatted Text</strong>unformatted text</div>

I've tried the suggested technique of mapping the object:

@RootElement("div")
class Root{

@Element(name="strong", targetType=String.class)
@Text
List<String> elements;
}

But all I get back is a list with 2 null elements.

Can anyone spot any obvious mistakes/suggest an alternative
implementation?

Thanks

Richard Hague

unread,
Jan 12, 2010, 10:12:21 AM1/12/10
to xmappr
Also noticed that I'm having some problems mapping a custom object to
a list:

@RootElement("Document")
public class XmapprKmlDocument {

@Element("Placemark")
private List<XmapprKmlPlacemark> placemarks;

placemarks seems to be null when parsed.

Peter Knego

unread,
Jan 12, 2010, 10:17:00 AM1/12/10
to xma...@googlegroups.com
Field "element" has package access, so Xmappr can not acces this field. This throws an exception so you should see it - check that you are not silently discarding exceptions somewhere. This field has to be either public or have getters/setters.

This works for me:

public class Foo {

    public static String xml = "<div><strong>Formatted Text</strong>unformatted text</div>";

    public static void main(String[] args) {
        StringReader reader = new StringReader(xml);
        Xmappr xmappr = new Xmappr(Root.class);
        xmappr.setPrettyPrint(true);

        Root root = (Root) xmappr.fromXML(reader);

        System.out.println("");
    }

    @RootElement("div")
    public static class Root {

        @Element(name = "strong", targetType = String.class)
        @Text
        public List<String> elements;
    }
}

Please note that Xmappr will not be able to marshall this to XML, since both <strong> and XML text are mapped to Strings in the same Collection. On marshalling Xmappr will not know which to produce. The easiest solution is to have a simple Strong class, so that element collection would contain Strings and Strongs objects.

Peter

Richard Hague

unread,
Jan 12, 2010, 10:25:04 AM1/12/10
to xmappr
Ah..

So my strong class would be:

@RootElement
class Strong
{
@Text
String text;
}

I'll have a look at my document class again. The list placemarks had
getters and setters so I might have missed something.

Thanks for replying!

Richard

Peter Knego

unread,
Jan 12, 2010, 10:30:36 AM1/12/10
to xma...@googlegroups.com
Class Strong is not mapped to root element. It should be:

class Strong{
  @Text
  public String text;
}

@RootElement("div")
public class Root {

       @Element(name = "strong", targetType = Strong.class)
       @Text
        public List elements;  // it'll contain both String and Strong classes
}

Peter

Peter Knego

unread,
Jan 12, 2010, 10:40:04 AM1/12/10
to xma...@googlegroups.com
You could also do:


@RootElement("div")
public class Root {

       @Element("*")
       @Text
        public List elements;  // it'll contain both String and DomElement classes
}

This will map text and all XML subelements. Text will become String, and subelements will become DomElement. DomElement is a simple holder clas that can store whole XML subelements (ant their attribute, text and subelements).

Richard Hague

unread,
Jan 12, 2010, 10:45:37 AM1/12/10
to xmappr
Thats probably caused my other issue as well.

I've been putting root element annotation on all my classes! I thought
you had to annotate the root element (ie the highest level) for each
class. In other words I thought the root element was relative to the
class not the the whole xml document!

This is how I've been doing my annotation as I thought each tag was a
the root element for that class....

@RootElement
kml
@RootElement
document
@RootElement
Placemark

Will change it so hopefully all my other niggles will go away!

On Jan 12, 3:30 pm, Peter Knego <pe...@knego.net> wrote:
> Class Strong is not mapped to root element. It should be:
>
> class Strong{
>   @Text
>   public String text;
>
> }
>
> @RootElement("div")
> public class Root {
>
>        @Element(name = "strong", targetType = Strong.class)
>        @Text
>         public List elements;  // it'll contain both String and Strong
> classes
>
> }
>
> Peter
>

Peter Knego

unread,
Jan 12, 2010, 10:47:18 AM1/12/10
to xma...@googlegroups.com
Yes, @RootElement maps root XML element to a class, so it's only needed on one class.

This test does something similar to what you are trying to do:

http://code.google.com/p/xmappr/source/browse/trunk/src/test/java/org/xmappr/MultielementCollectionTest.java

The collection now has getters/setters.

Peter

Peter Knego

unread,
Jan 12, 2010, 10:53:07 AM1/12/10
to xma...@googlegroups.com
You can mix-and-match named mappings with wildcard mappings:


@RootElement("div")
 public static class Root {

        @Element("*")

        @Element(name = "strong", targetType = Strong.class)
        @Text
        public List elements;
}

This will map all XML text to Strings, all <strong> to Strong and all other XML subelements to DomElement.

This approach is described in http://code.google.com/p/xmappr/wiki/PreservingUnmappedelements#Gradual_development

Richard Hague

unread,
Jan 13, 2010, 4:44:32 AM1/13/10
to xmappr
Hi Peter,
Should I be able to map:

rootelement
element
subelement
childelement1
childelement2
childelement3
subelement

For some reason, the subelement seems to be returning null.

I'd mapped the subelements to a collection within element (there can
be multiple occurrences) and I'd mapped the childelements as follows:

@Element(name="name", targetType=childelement1.class),
@Element(name="description", targetType=childelement2.class),
@Element(name="Point", targetType=childelement3.class)
private List contents;

in other words

$RootElement
rootelement
{
$Element
Element element
}

Element
{
$Element("subelement", targetType=subelement.class)
List subelements
}

Subelement
{
@Element(name="name", targetType=childelement1.class),
@Element(name="description", targetType=childelement2.class),
@Element(name="Point", targetType=childelement3.class)
private List contents;
}

etc .....

Thanks in advance.

On Jan 12, 3:53 pm, Peter Knego <pe...@knego.net> wrote:
> You can mix-and-match named mappings with wildcard mappings:
>
> @RootElement("div")
>  public static class Root {
>
>         @Element("*")
>         @Element(name = "strong", targetType = Strong.class)
>         @Text
>         public List elements;
>
> }
>
> This will map all XML text to Strings, all <strong> to Strong and all other
> XML subelements to DomElement.
>

> This approach is described inhttp://code.google.com/p/xmappr/wiki/PreservingUnmappedelements#Gradu...


>
> On Tue, Jan 12, 2010 at 4:47 PM, Peter Knego <pe...@knego.net> wrote:
> > Yes, @RootElement maps root XML element to a class, so it's only needed on
> > one class.
>
> > This test does something similar to what you are trying to do:
>

> >http://code.google.com/p/xmappr/source/browse/trunk/src/test/java/org...


>
> > The collection now has getters/setters.
>
> > Peter
>

Peter Knego

unread,
Jan 13, 2010, 4:59:29 AM1/13/10
to xma...@googlegroups.com
Yes that's possible. Are you getting any Exceptions?

I see now that I made a mistake with the example - multiple @Element annotations must be wrapped in @Elements() annotation:

          @Elements({

               @Element(name="name", targetType=childelement1.class),
           @Element(name="description", targetType=childelement2.class),
           @Element(name="Point", targetType=childelement3.class)
           })
       private List contents;

Also contents field needs getter/setter.

If it still gives you trouble you can send me example code and XML in a zip file and I'll make it work.

Richard Hague

unread,
Jan 13, 2010, 6:55:44 AM1/13/10
to xmappr
Hi Peter,
I've written a unit test that mimics my xml mapping
structure. Can I send it to you please?

Cheers

On Jan 13, 9:59 am, Peter Knego <pe...@knego.net> wrote:
> Yes that's possible. Are you getting any Exceptions?
>
> I see now that I made a mistake with the example - multiple @Element
> annotations must be wrapped in @Elements() annotation:
>
>           @Elements({
>                @Element(name="name", targetType=childelement1.class),
>
> >            @Element(name="description", targetType=childelement2.class),
> >            @Element(name="Point", targetType=childelement3.class)
>
>            })
>
> >        private List contents;
>
> Also contents field needs getter/setter.
>
> If it still gives you trouble you can send me example code and XML in a zip
> file and I'll make it work.
>

Peter Knego

unread,
Jan 13, 2010, 7:28:24 AM1/13/10
to xma...@googlegroups.com

Yes please, so send it.

On 13 Jan 2010 12:55, "Richard Hague" <rich...@googlemail.com> wrote:

Hi Peter,
             I've written a unit test that mimics my xml mapping
structure. Can I send it to you please?

Cheers

On Jan 13, 9:59 am, Peter Knego <pe...@knego.net> wrote: > Yes that's possible. Are you getting any...

> On Wed, Jan 13, 2010 at 10:44 AM, Richard Hague <richha...@googlemail.com>wrote:

> > > Hi Peter, > >               Should I be able to map: > > > rootelement > >                   e...

Richard Hague

unread,
Jan 13, 2010, 8:27:27 AM1/13/10
to xmappr
Sent it to your email Peter.

On Jan 13, 12:28 pm, Peter Knego <pe...@knego.net> wrote:
> Yes please, so send it.
>

Peter Knego

unread,
Jan 13, 2010, 10:45:23 AM1/13/10
to xma...@googlegroups.com
Richard,

you did everything right. There is a small deficiency in Xmappr: namespace definitions are not inherited by "lower" mappings, unlike XML where xmlns definitions are inherited by subelements.

@Namespaces("http://www.opengis.net/kml/2.2")
@RootElement("root2")
public class Root2{

Here namespace mapping is only defined for Root2 class and fields inside it. I'm looking for ways to resolve this in the near future.
However, you can define namespace mappings configuration-wide:

xmappr.addNamespace("http://www.opengis.net/kml/2.2") //default namespace
or
xmappr.addNamespace("prefix","http://www.opengis.net/kml/2.2") // namespace with prefix

Namespace mappings can still be overriden per class. This is described here http://code.google.com/p/xmappr/wiki/NameSpaces#Namespaces_defined_in_configuration

With this line added your example works.

Peter

Peter Knego

unread,
Jan 13, 2010, 10:49:37 AM1/13/10
to xma...@googlegroups.com
One way to check if configuration is ok:

String mappings = xmappr.getXmlConfiguration(MappedClass.class)

This will create XML with all the mappings of the MappedClass. You can then inspect it to see what is missing.

Another way is to create object tree and write it out to XML.

xmappr.toXML(objectTree, writer)  // print out the writer and inspect generated XML

Peter

Richard Hague

unread,
Jan 13, 2010, 10:56:05 AM1/13/10
to xmappr
I'll give it a go Peter. I hope I can get it working soon!

Many thanks,

Richard

> >http://code.google.com/p/xmappr/wiki/NameSpaces#Namespaces_defined_in...


>
> > With this line added your example works.
>
> > Peter
>

Peter Knego

unread,
Jan 13, 2010, 11:02:00 AM1/13/10
to xma...@googlegroups.com
here is the code..
testxmappr 2.zip

Richard Hague

unread,
Jan 14, 2010, 4:19:57 AM1/14/10
to xmappr
Hi Peter,
I've managed to get my code working now. It was a
combination of incorrect element annotation and the namespaces that
was my downfall.

Thanks for your help,

Richard

On Jan 13, 4:02 pm, Peter Knego <pe...@knego.net> wrote:
> here is the code..
>

>  testxmappr 2.zip
> 25KViewDownload

Reply all
Reply to author
Forward
0 new messages