代码之家  ›  专栏  ›  技术社区  ›  Christian

使用XmlElements和XmlAdapter的JAXB/Moxy

  •  2
  • Christian  · 技术社区  · 11 年前

    我尽量做到这里面描述的 question 使用MOXy。 但我有一些不同的行为,但结果是一样的。它不起作用。

    我使用@XmlElements,并为每个concret类添加了一个带有名称和类型的@XmlElement。 对于所有没有适配器的类,它都能完美工作,但对于有PolyAdapter的类则不然。

    在我的情况下,将调用适配器来封送对象。但它所写的只是xml文件中的一个类似这样的条目。

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

    而且根本不会叫那个理发师。

    我测试了MOXy 2.5.0和当前的RC3 2.5.1,在这两种情况下,结果是相同的。 我还启用了调试输出,没有任何警告或错误。

    或者也许还有另一种方法可以让它发挥作用

    更新1: 提供模型

    我创建了一个小例子,展示了我对MOXy 2.5.0的问题

    接口:

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

    不带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 );
      }
    }
    

    使用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;
      } 
    }
    }
    

    简单容器类:

    @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<>();
    }
    

    主要内容:

    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 */
      }
    }
    

    我还更新了我上面描述的结果。谢谢。

    更新2: 简化包装

    即使这个简单的包装只包含一个属性,也不会有任何变化。

    @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;
        }
      }
    }
    

    因此,如果有人知道哪里出了问题,那将是非常有帮助的。 谢谢 基督教的

    1 回复  |  直到 11 年前
        1
  •  1
  •   bdoughan    11 年前

    注: 我是 EclipseLink JAXB (MOXy) 领导和 JAXB (JSR-222) 专家组。

    您遇到的问题是由于 CCPWrapper 类映射不正确。不允许将两个字段/财产映射为 @XmlAnyElement .

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

    当我修改您的代码以删除 @XmlAnyElement(XmlAnyElement) 注释一切似乎都正常工作。


    更新#1

    谢谢你的回答。但不,我仍然得到相同的输出。我甚至 删除了带有XmlAnyElement注释的两个列表,只添加了 简单的XmlAttribute。输出不变。查看更新的 有问题的包装类。

    我只是注意到,当我尝试您的代码时,我添加了一个类型为 CPlexPoly FooContainer 班除了我之前的建议之外 XmlAdapter 应用于 container 领域

    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<>();
    
    }
    

    你可以做一些类似的变通办法。我已经打开了以下错误,您可以使用它来跟踪我们在真正修复方面的进展:


    更新#2

    一个更好的解决方法是包括 CCP包装 用于创建 JAXBContext 如下所示:

        context = JAXBContext.newInstance( FooContainer.class, CCPWrapper.class );