JaxB XmlSeeAlso,XmlElementRef,带有类的newInstance

时间:2013-02-18 07:19:43

标签: jaxb moxy

我的问题的答案

让我进一步了解有关使用XmlSeeAlso,XmlElementReference以及JaxbContext.newInstance中涉及的类规范的问题的详细信息。

我开始尝试回答这个问题:

我在下面创建了Junit测试。为此,我来到了这里:

在这种状态下,代码可编译并运行 - 它会编组o.k.但不像预期的那样流氓。 元帅的结果是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message>
    <BasicInfo ref="id001"/>
</Message>

Unmarshalling不会像适配器中的代码那样创建BasicInfo:

   public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
     BasicInfo binfo=new BasicInfo();
     binfo.id=info.ref;
     return binfo;
   }

我发现这一切都非常令人困惑 - 事情似乎有些矛盾,而且大多数情况下并没有像我期望的那样对JaxB设置有所帮助。大多数组合不起作用,那些起作用的组合还没有给出完整的结果。我认为这也取决于所使用的版本和实现。

  1. 要做到这一点需要做些什么?
  2. 什么是XmlElementRef,XmlSeeAlso和JaxbContext newInstance引用必要类的最小集合?
  3. 如何查看正在使用的JaxB实施?我正在使用EclipseLink 2.3.2?而且我不确定这是否使用MoxY。
  4. 更新 经过考虑:

    1. JAXB xsi:type subclass unmarshalling not working
    2. http://www.java.net/node/654579
    3. http://jaxb.java.net/faq/#jaxb_version
    4. http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm
    5. Can JAXB marshal by containment at first then marshal by @XmlIDREF for subsequent references?
    6. 以下修改后的代码接近预期。

      尝试时出现的错误是:

      javax.xml.bind.MarshalException
       - with linked exception:
      [com.sun.istack.SAXException2: unable to marshal type "com.bitplan.storage.jaxb.TestRefId$BasicInfo$BasicInfoRef" as an element because it is missing an @XmlRootElement annotation]
          at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
      

      这段代码有很多评论可以评论进出,以找出如果有人尝试会发生什么...... 我仍然不知道避免多余注释的最佳解决方案是什么。

      修改后的代码:

      package com.bitplan.storage.jaxb;
      
      import static org.junit.Assert.*;
      
      import java.io.File;
      import java.io.FileWriter;
      import java.io.IOException;
      import java.io.PrintWriter;
      import java.io.StringReader;
      import java.io.StringWriter;
      import java.util.ArrayList;
      import java.util.List;
      
      import javax.xml.bind.JAXBContext;
      import javax.xml.bind.JAXBException;
      import javax.xml.bind.Marshaller;
      import javax.xml.bind.Unmarshaller;
      import javax.xml.bind.annotation.XmlAttribute;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlElementWrapper;
      import javax.xml.bind.annotation.XmlRootElement;
      import javax.xml.bind.annotation.XmlSeeAlso;
      import javax.xml.bind.annotation.adapters.XmlAdapter;
      import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
      import javax.xml.bind.annotation.XmlAccessorType;
      import javax.xml.bind.annotation.XmlAccessType;
      
      import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
      import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
      import org.junit.Test;
      
      /**
       * Test the Reference/Id handling for Jaxb
       * 
       * @author wf
       * 
       */
      public class TestRefId {
      
          @XmlDiscriminatorNode("@reforid")
          @XmlAccessorType(XmlAccessType.FIELD)
          @XmlJavaTypeAdapter(RefOrId.Adapter.class)
          public static class RefOrId {
              @XmlAttribute(name = "reforid")
              public String reforid;
      
              public RefOrId() {
              }
      
              @XmlAttribute
              public String id;
      
              @XmlAttribute
              public String ref;
      
              public static class Adapter extends XmlAdapter<RefOrId, RefOrId> {
      
                  @Override
                  public RefOrId marshal(RefOrId idref) throws Exception {
                      if (idref == null)
                          return null;
                      System.out.println("marshalling " + idref.getClass().getSimpleName()
                              + " reforid:" + idref.reforid);
                      if (idref instanceof Ref)
                          return new Id(((Ref) idref).ref);
                      return idref;
                  }
      
                  @Override
                  public RefOrId unmarshal(RefOrId idref) throws Exception {
                      System.out.println("unmarshalling " + idref.getClass().getSimpleName()
                              + " reforid:" + idref.reforid);
                      return idref;
                  }
              }
          }
      
          @XmlDiscriminatorValue("id")
          @XmlAccessorType(XmlAccessType.FIELD)
          public static class Id extends RefOrId {
              public Id() {
                  reforid = "id";
              }
      
              public Id(String pId) {
                  this();
                  this.id = pId;
              }
      
          }
      
          @XmlDiscriminatorValue("ref")
          @XmlAccessorType(XmlAccessType.FIELD)
          public static class Ref extends RefOrId {
              public Ref() {
                  reforid = "ref";
              }
      
              public Ref(String pRef) {
                  this();
                  this.ref = pRef;
              }
          }
      
          /*
           * https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a
           * -class-without-no-args-constructor-which-has
           */
          @XmlRootElement(name = "BasicInfo")
          public static class BasicInfo {
              public BasicInfo() {
              };
      
              public BasicInfo(String pId, String pInfo) {
                  this.id = new Id(pId);
                  this.basic = pInfo;
              }
      
              // @XmlTransient
              public RefOrId id;
              public String basic;
          }
      
          @XmlRootElement(name = "SpecificInfoA")
          // @XmlJavaTypeAdapter(BasicInfo.Adapter.class)
          public static class SpecificInfoA extends BasicInfo {
      
              public SpecificInfoA() {
              };
      
              public SpecificInfoA(String pId, String pInfo, String pInfoA) {
                  super(pId, pInfo);
                  infoA = pInfoA;
              }
      
              public String infoA;
          }
      
          @XmlRootElement(name = "Message")
          @XmlAccessorType(XmlAccessType.FIELD)
          @XmlSeeAlso({ SpecificInfoA.class, BasicInfo.class })
          public static class Message {
      
              public Message() {
              };
      
              public Message(BasicInfo pBasicInfo) {
                  basicInfos.add(pBasicInfo);
              }
      
              // @XmlJavaTypeAdapter(BasicInfo.Adapter.class)
              // @XmlElement(name="basicInfo",type=BasicInfoRef.class)
              @XmlElementWrapper(name = "infos")
              @XmlElement(name = "info")
              public List<BasicInfo> basicInfos = new ArrayList<BasicInfo>();
          }
      
          /**
           * marshal the given message
           * 
           * @param jaxbContext
           * @param message
           * @return - the xml string
           * @throws JAXBException
           */
          public String marshalMessage(JAXBContext jaxbContext, Message message)
                  throws JAXBException {
              Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
              // output pretty printed
              jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              StringWriter sw = new StringWriter();
              jaxbMarshaller.marshal(message, sw);
              String xml = sw.toString();
              return xml;
          }
      
          /**
           * test marshalling and umarshalling a message
           * 
           * @param message
           * @throws JAXBException
           */
          public void testMessage(Message message) throws JAXBException {
              @SuppressWarnings("rawtypes")
              Class[] classes = { Message.class, SpecificInfoA.class, BasicInfo.class }; // BasicInfo.class,};
              // https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
              // https://stackoverflow.com/questions/11966714/xmljavatypeadapter-not-being-detected
              JAXBContext jaxbContext = JAXBContext.newInstance(classes);
      
              String xml = marshalMessage(jaxbContext, message);
              System.out.println(xml);
      
              Unmarshaller u = jaxbContext.createUnmarshaller();
              Message result = (Message) u.unmarshal(new StringReader(xml));
              assertNotNull(result);
              assertNotNull(message.basicInfos);
              for (BasicInfo binfo : message.basicInfos) {
                  RefOrId id = binfo.id;
                  assertNotNull(id);
                  System.out.println("basicInfo-id " + id.getClass().getSimpleName()
                          + " for reforid " + id.reforid);
                  // assertEquals(message.basicInfo.id, result.basicInfo.id);
              }
      
              xml = marshalMessage(jaxbContext, result);
              System.out.println(xml);
          }
      
          /**
           * switch Moxy
           * 
           * @param on
           * @throws IOException
           */
          public void switchMoxy(boolean on) throws IOException {
              File moxySwitch = new File(
                      "src/test/java/com/bitplan/storage/jaxb/jaxb.properties");
              if (on) {
                  PrintWriter pw = new PrintWriter(new FileWriter(moxySwitch));
                  pw.println("javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory");
                  pw.close();
              } else {
                  moxySwitch.delete();
              }
          }
      
          @Test
          public void testStackOverflow8292427() throws JAXBException, IOException {
              boolean[] moxyOnList = { false };
              for (boolean moxyOn : moxyOnList) {
                  switchMoxy(moxyOn);
                  System.out.println("Moxy used: " + moxyOn);
                  Message message = new Message(new BasicInfo("basicId001",
                          "basicValue for basic Info"));
                  message.basicInfos.add(new SpecificInfoA("specificId002",
                          "basicValue for specific Info", "specific info"));
                  message.basicInfos.add(new SpecificInfoA("specificId002",
                          "basicValue for specific Info", "specific info"));
                  testMessage(message);
              }
          }
      
      }
      

      这是上面提到的Junit测试(更新前):

          package com.bitplan.storage.jaxb;
          import static org.junit.Assert.*;
          import java.io.StringReader;
          import java.io.StringWriter;   
          import javax.xml.bind.JAXBContext;
          import javax.xml.bind.JAXBException;
          import javax.xml.bind.Marshaller;
          import javax.xml.bind.Unmarshaller;
          import javax.xml.bind.annotation.XmlAttribute;
          import javax.xml.bind.annotation.XmlElementRef;
          import javax.xml.bind.annotation.XmlElementRefs;
          import javax.xml.bind.annotation.XmlRootElement;
          import javax.xml.bind.annotation.XmlSeeAlso;
          import javax.xml.bind.annotation.adapters.XmlAdapter;
          import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
          import javax.xml.bind.annotation.XmlAccessorType;
          import javax.xml.bind.annotation.XmlAccessType;
      
          import org.junit.Test;
      
          // Test the Reference/Id handling for Jaxb
          public class TestRefId {
      
          /* 
           * https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a-class-without-no-args-constructor-which-has
           */
          @XmlRootElement(name="BasicInfo")
          @XmlJavaTypeAdapter(BasicInfo.Adapter.class)
          public static class BasicInfo {
      
             @XmlRootElement(name="BasicInfo")
               @XmlAccessorType(XmlAccessType.FIELD)
               public static class BasicInfoRef {
                @XmlAttribute(name = "ref")
                    public String ref;
               }
      
               public static class Adapter extends  XmlAdapter<BasicInfoRef,BasicInfo>{
      
                  @Override
                  public BasicInfoRef marshal(BasicInfo info) throws Exception {
                      BasicInfoRef infoRef = new BasicInfoRef();
                      infoRef.ref=info.id;
                      return infoRef;
                  }
      
                  @Override
                  public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
                      BasicInfo binfo=new BasicInfo();
                      binfo.id=info.ref;
                      return binfo;
                  }
      
               } // Adapter
               public String id;
               public String basic;
          }
      
          @XmlJavaTypeAdapter(BasicInfo.Adapter.class)
          public static class SpecificInfoA extends BasicInfo {
              public String infoA;
          }
      
          @XmlRootElement(name="Message")
          @XmlAccessorType(XmlAccessType.FIELD)
          @XmlSeeAlso({SpecificInfoA.class,BasicInfo.class})
          public static class Message {
              // https://stackoverflow.com/questions/3107548/xmljavatypeadapter-w-inheritance
              @XmlElementRefs({
                 @XmlElementRef(name="basic", type=BasicInfo.class),
                 // @XmlElementRef(name="infoA", type=SpecificInfoA.class)
              })
          public BasicInfo basicInfo;
          }
      
          @Test
          public void testStackOverflow8292427() throws JAXBException {
              Message message=new Message();
              SpecificInfoA info=new SpecificInfoA();
              info.id="id001";
              info.basic="basicValue";
              info.infoA="infoAValue";
              message.basicInfo=info;
              @SuppressWarnings("rawtypes")
              Class[] classes= {Message.class,SpecificInfoA.class,BasicInfo.class,BasicInfo.BasicInfoRef.class}; // BasicInfo.class,};
              // https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
              JAXBContext jaxbContext = JAXBContext.newInstance(classes);
      
              Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
              // output pretty printed
              jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              StringWriter sw = new StringWriter();
              jaxbMarshaller.marshal(message, sw);
              String xml=sw.toString();
              System.out.println(xml);
      
              Unmarshaller u = jaxbContext.createUnmarshaller();
              Message result = (Message) u.unmarshal(new StringReader(xml));
              assertNotNull(result);
              assertNotNull("basicInfo should not be null",result.basicInfo);
              assertEquals("id001",result.basicInfo.id);
             } 
          }
      

0 个答案:

没有答案