Question

I try to do exactly what is descripted in this question with MOXy. But i have some different behavior but the result is the same. It doesn't work.

I use @XmlElements and for each concret class I added a @XmlElement with name and type. For all classes without an adapter it works perfect but not for the class with the PolyAdapter.

In my case the adapter will be called to marshal the object. But all it writes is an entry like this into the xml file.

<?xml version="1.0" encoding="UTF-8"?>
<fooContainer>
  <poly PolyName="PolyN$1">ABCBCD</poly>
  <cPlexPoly>moxy.CPlexPoly$CPlexPolyAdapter$CCPWrapper@480bdb19</cPlexPoly>
</fooContainer>

And the unmarshaller won't be called at all.

I tested MOXy 2.5.0 and the current RC3 2.5.1 in both cases the results are the same. I also enabled the debug output and there are no warnings or error.

Or maybe there is another way to get this working

Update 1: Providing model

I created a small example showing my problem with MOXy 2.5.0

Interface:

@XmlRootElement
public interface Foo {
  public List<String> getCoordinates();
}

Class without XmlAdapter:

@XmlRootElement(name="poly")
public class Poly implements Foo {
  @XmlAttribute
  String PolyName = "PolyN$1";

  @XmlAnyElement
  private List<String> coordinates = new LinkedList<>();

  @Override
  public List<String> getCoordinates() {
    return coordinates;
  }

  public void addCoordinate( final String poly ) {
    coordinates.add( poly );
  }
}

Class with XmlAdapter:

@XmlJavaTypeAdapter(CPlexPolyAdapter.class)
public class CPlexPoly implements Foo {
private Map<String, String> values = new HashMap<>();

@Override
public List<String> getCoordinates() {
  return new ArrayList<>(values.values());
}

public void addCoordinate( final String poly ) {
  values.put( Integer.toString( poly.hashCode()), poly );
}

public List<String> getKeys() {
  return new ArrayList<>(values.keySet());
}


public static class CPlexPolyAdapter extends XmlAdapter<CCPWrapper, CPlexPoly> {

  @XmlRootElement(name="wrapper")
  public static class CCPWrapper {
    @XmlAnyElement
    List<String> keys = new ArrayList<>();
    @XmlAnyElement
    List<String> values = new ArrayList<>();
  }

  @Override
  public CPlexPoly unmarshal(CCPWrapper v) throws Exception {
    // Won't be called. 
    return null;
  }

  @Override
  public CCPWrapper marshal(CPlexPoly v) throws Exception {
    if( v == null ) {
      return null;
    }

    CCPWrapper wrapper = new CCPWrapper();
    wrapper.values.addAll( v.getCoordinates() );
    wrapper.keys.addAll( v.getKeys() );

    return wrapper;
  } 
}
}

Simple container class:

@XmlRootElement(name="fooContainer") 
public class FooContainer {

  @XmlElements({
    @XmlElement(name = "poly", type = Poly.class),
    @XmlElement(name = "cPlexPoly", type = CPlexPoly.class)
  })
  public List<Foo> container = new LinkedList<>();
}

Main:

public class Main {
  public static void main(String[] args) {
    FooContainer fc = new FooContainer();

    Poly poly = new Poly();
    poly.addCoordinate("ABC");
    poly.addCoordinate("BCD");

    CPlexPoly cpoly = new CPlexPoly();
    cpoly.addCoordinate("XYZ");
    cpoly.addCoordinate("WXY");

    fc.container.add( poly );
    fc.container.add( cpoly );

    /* Marshall */
    JAXBContext context;
    try {
      context = JAXBContext.newInstance( FooContainer.class );
      Marshaller marshaller = context.createMarshaller();
      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
      marshaller.marshal( fc, new FileWriter("fc.xml") );
    } 
    catch (JAXBException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    /* UnMarshall */
  }
}

I also updated the result i described above. Thx.

Update 2: Simplified Wrapper

Even with this simple wrapper containing nothing more than an attribute, nothing changes.

@XmlJavaTypeAdapter(CPlexPolyAdapter.class)
public class CPlexPoly implements Foo {

  private Map<String, String> values = new HashMap<>();

  @Override
  public List<String> getCoordinates() {
    return new ArrayList<>(values.values());
  }

  public void addCoordinate( final String poly ) {
    values.put( Integer.toString( poly.hashCode()), poly );
  }

  public List<String> getKeys() {
    return new ArrayList<>(values.keySet());
  }


  public static class CPlexPolyAdapter extends XmlAdapter<CCPWrapper, CPlexPoly> {

    @XmlRootElement(name="wrapper")
    public static class CCPWrapper {

      @XmlAttribute
      String test = "test";
    }

    @Override
    public CPlexPoly unmarshal(CCPWrapper v) throws Exception {
      // Won't be called. 
      return null;
    }

    @Override
    public CCPWrapper marshal(CPlexPoly v) throws Exception {
      if( v == null ) {
        return null;
      }

      CCPWrapper wrapper = new CCPWrapper();
      return wrapper;
    }
  }
}

So if someone has any idea what is wrong it would be very helpful. Thanks Christian

Was it helpful?

Solution

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

The problem you are encountering is due to the CCPWrapper class being mapped incorrectly. You are not allowed to have two fields/properties mapped with @XmlAnyElement.

    @XmlRootElement(name = "wrapper")
    public static class CCPWrapper {
        @XmlAnyElement
        List<String> keys = new ArrayList<>();
        @XmlAnyElement
        List<String> values = new ArrayList<>();
    }

When I modified your code to remove one of the @XmlAnyElement annotations everything appeared to work correctly.


UPDATE #1

Thank you for your answer. But no I still get the same output. I even removed both lists with the XmlAnyElement annotation and just added a simple XmlAttribute. The output doesn't change. See the updated Wrapper class in question.

I just noticed that when I was trying out your code that I had added a field of type CPlexPoly to the FooContainer class. This in addition to my previous suggestion caused the XmlAdapter to be applied to the container field.

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement(name = "fooContainer")
public class FooContainer {

    @XmlElement
    private final CPlexPoly workaround = null;

    @XmlElements({ 
        @XmlElement(name = "poly", type = Poly.class),
        @XmlElement(name = "cPlexPoly", type = CPlexPoly.class) 
    })
    public List<Foo> container = new LinkedList<>();

}

You could do something similar as a workaround. I have opened the following bug that you can use to track our progress on the real fix:


UPDATE #2

A better workaround would be to include the CCPWrapper class in the classes used to create the JAXBContext as follows:

    context = JAXBContext.newInstance( FooContainer.class, CCPWrapper.class );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top