tags:

views:

30

answers:

1

Like the title says, I have two different objects that implement the same interface and I want to add them to the same collection.

My XML looks like:

  <root>
   <item-list>
     <item>
         <type1>
            <id>1</id>
        </type1>
    </item>
    <item>
        <type2>
            <id>2</id>
            <other>1</other>
        </type2>
    </item>
  </item-list>      
</root>

The tag under item can either be type1 or type2 at any time. They have similar values but, as you see, type2 has another field.

My Interface is:

  public interface Item {

   public String getID();
}

My three classes:

public class MyObj {
   private ArrayList<Item> items = new ArrayList<Item>();

   public List<Item> getItems() {
    return items;
   }
}
 public class Type1 implements Item{
   private String id;

    @Override
    public String getID() {
      return id;
   }
}

 public class Type2 implements Item {
    private String id;
    private String other;

    @Override
    public String getID() {
       return id;
    }

    public String getOther() {
       return other;
    }
 }

My binding looks like this:

<binding direction="input" >

<mapping name="item-list" class="jibx.woe.MyObj" >

    <collection field="items" >
        <structure name="item" choice="true" ordered="false">

            <structure name="type1" map-as="type1"/>

            <structure name="type2" map-as="type2"/>

        </structure>

    </collection>

</mapping>
<mapping class="jibx.woe.Type1" abstract="true" type-name="type1">
    <value name="id" field="id"/>
</mapping>

<mapping class="jibx.woe.Type2" abstract="true" type-name="type2">
    <value name="id" field="id"/>
    <value name="other" field="other"/>
</mapping>

And when I run it, I get the this error:

*** Error during code generation for file 'C:\Projects\IdeaProjects\jibx-woe\src \main\config\woe-binding.xml' -
 this may be due to an error in your binding or classpath, or to an error in the JiBX   code ***

 Internal error: Expected jibx.woe.Type1 on stack, found java.lang.Object full stack:
  0: java.lang.Object
  1: java.lang.Object
  2: org.jibx.runtime.impl.UnmarshallingContext

So I thought I add an item type to the collection, so now my collection section looks like:

   <collection field="items" item-type="jibx.woe.Item">
        <structure name="item" choice="true" ordered="false" >

            <structure name="type1" map-as="type1"/>

            <structure name="type2" map-as="type2"/>

        </structure>

    </collection>

And the error now says:

  Internal error: Expected jibx.woe.Type1 on stack, found jibx.woe.Item
  full stack:
    0: jibx.woe.Item
    1: jibx.woe.Item
    2: org.jibx.runtime.impl.UnmarshallingContext

So now I add the following method to MyObj:

  public void addItem(Object type) {
      items.add((Item)type);
   }

And changed the collection tag to:

  <collection add-method="addItem">

And get same same error as my first attempt:

Internal error: Expected jibx.woe.Type1 on stack, found java.lang.Object
 full stack:
   0: java.lang.Object
   1: java.lang.Object
   2: org.jibx.runtime.impl.UnmarshallingContext

And now I'm out of ideas. How can I get this to work?

EDIT

Based on Archie's recommendation below, I changed my binding to:

<mapping name="root" class="jibx.woe.MyObj">

<structure name="item-list" >

    <collection field="items">
        <structure name="item" /> >

    </collection>
 </structure>
</mapping>

<mapping name="type1" class="jibx.woe.Type1" >
    <value name="id" field="id"/>
</mapping>

<mapping name="type2" class="jibx.woe.Type2" >
    <value name="id" field="id"/>
    <value name="other" field="other"/>
</mapping>


</binding>

And now the binding works without stack errors (hurray!) but now when I get a item from my list a la:

    List<Item> items = woe.getItems();
    Type1 item = (Type1) items.get(0);

I get:

    java.lang.ClassCastException: java.lang.Object cannot be cast to jibx.woe.Type1

On my second line above. If I print the object: System.out.println(items.get(0));

It says: java.lang.Object@1be2d65

So it's a plain object, not an Element or a Type1 (or Type2) at all!

I did try the "extends" method Archie proposed, but it didn't change anything -- I got the same result.

A: 

Create an abstract mapping for Item. Then create two concrete mappings for Type1 and Type2, each extending the Item mapping. In your items collection, make each element an Item (get rid of the choice stuff).

Note in JiBX the 'extends' keyword has nothing to do with Java classes, it means 'can be substituted for'.

Limitation: you will have to give Type1 and Type2 two different XML tag names (e.g., "type1" and "type2") so JiBX can distinguish them when unmarshalling. This is the problem with what you are currently doing. E.g., when JiBX sees an tag, how will it know which Java class to instantiate?

Archie
See my output above. We are so close now . . . I also have little to no control over the XML that I am parsing, so I need to figure out how to do it with the structure above. But I agree -- the style I'm given isn't making this easier.
MikeHoss