3

有没有办法配置 @XmlTransient JPA2.0 注释,它只会在 Java-Object 序列化为 xml 时阻止 JAXB 机制,而不是在传入的 XML 转换为 java 对象时?

背景:我有一个讲 XML 的 REST api。有一个端点可以创建一个新的 Attachment 对象。正如我们所说的附件,这个类中有一个 byte[] 字段。在附件的进一步列表中,我不想提供每个附件的 byte[] 内容。

@Entity
@XmlRootElement
public class Attachment {

private String name;

private String mimeType;

private byte[] dataPart;

public String getName() {
    return name;
}

public void setName( String name ) {
    this.name = name;
}

public String getMimeType() {
    return mimeType;
}

public void setMimeType( String mimeType ) {
    this.mimeType = mimeType;
}

public byte[] getDataPart() {

    return dataPart.clone();
}

public void setDataPart( byte[] dataPart ) {
    this.dataPart = dataPart.clone();
}
}

所以,当我用 XmlTransient 标记 getDataPart() 时,传入的 byte[] 的数据被忽略并设置为 null -> 它丢失了。有人知道如何指定 XmlTransient 的方向吗?

4

4 回答 4

1

I have to answer the question myself =) I solved this by using a custom XmlAdapter that will tranlsate the binary data only in one direction. This is still a hack and we are not using this any more. Reasons below.

Here is the adapter:

public class DuplexBase64MarshallAdapter extends XmlAdapter<String, byte[]> {

        /**
         * running the adapter in half duplex mode means, the incoming data is marshaled but the
         * outgoing data not.
         */
        public static final boolean HALF_DUPLEX = false;

        /**
         * Running the adapter in full duplex means, the incoming and outgoing data is marshalled.
         */
        public static final boolean FULL_DUPLEX = true;

        private boolean isFullDuplexMode;

        public DuplexBase64MarshallAdapter() {
            this.isFullDuplexMode = HALF_DUPLEX;
        }

        /**
         * Constructor
         * 
         * @param fullDuplex
         *            use {@link #HALF_DUPLEX} or {@link #FULL_DUPLEX}
         */
        public DuplexBase64MarshallAdapter( boolean fullDuplex ) {
            this.isFullDuplexMode = fullDuplex;
        }

        @Override
        public byte[] unmarshal( String v ) throws Exception {
            return Base64.decode( v );
        }

        /**
         * Return always an empty string. We do not want to deliver binary content here.
         */
        @Override
        public String marshal( byte[] v ) throws Exception {
            if( isFullDuplexMode ) {
                return Base64.encodeBytes( v );
            }
            return "";
        }

    }

The entity needs to be annotated with this adapter:

@Entity
@XmlRootElement
public class Attachment {

    private String name;

    private String mimeType;

    private byte[] dataPart;

    public String getName() {
        return name;
    }

    public void setName( String name ) {
        this.name = name;
    }

    public String getMimeType() {
        return mimeType;
    }

    public void setMimeType( String mimeType ) {
        this.mimeType = mimeType;
    }

    @XmlJavaTypeAdapter( DuplexBase64MarshallAdapter.class )
    public byte[] getDataPart() {

        return dataPart.clone();
    }

    public void setDataPart( byte[] dataPart ) {
        this.dataPart = dataPart.clone();
    }
}

This solution works as exepcted. But there is a drawback: One of the intentions was to not let hibernate load the binary data while processing/loading attachment data. But thats not how it works. The binary data is loaded by hibernate in this case because it is send to the XMLAdapter but not translated into base64 :(

于 2012-06-01T09:33:39.947 回答
0

I think your question is not about "yes, serialise it, but don't deserialize it", but rather: transport the data client --> server, but not server --> client. You do want your server to deserialize the incoming byte[] data, right? You just don't want to (always?) sent it out when returning the entity.

If that is the case, you can simply set it to null before you send out the entity. In your REST resource, do something like this:

@PersistenceContext(unitName = "myPersistenceUnit")
privat EntityManager em;

@Path("/upload")
@POST
public Response storeAttachment(JAXBElement<Attachment> jaxbAttachment) {
  Attachment attachment = jaxbAttachment.getValue();

  // store it in the DB
  em.persist(attachment);

  // detach it, so that modifications are no longer reflected in the database
  em.detach(attachment);

  // modify your guts out, it'll only affect this instance
  attachment.setDataPart(null);   // <-- NPE if you don't get rid of .clone() in your entity

  URI newUri = ...
  return Response.created(newUri).entity(attachment).build();
}
于 2012-05-31T08:26:22.577 回答
0

Just in case you do want to only serialize something, but never deserialize it, use @XmlAccessorType(XmlAccessType.FIELD) and only provide an @XmlElement annotated getter.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Something {

  public Integer foo;

  public Integer getFoo() {
    return foo;
  }

  public void setFoo(Integer foo) {
    this.foo = foo;
  }

  @XmlElement
  public Integer getBar() {
    return 42;
  }
}

(Assuming foo is set to 17), this will marshall into

<something>
  <foo>17</foo>
  <bar>42</bar>
</something>

If you deserialize that (with the same class definition), bar will not be set by the unmarshaller (since it cannot find a setter).

于 2012-05-31T08:33:57.573 回答
0

Hans, I had same problem. Your answer kind of put me in the right way. If the problem is only the serialization, this does the trick:

public class HalfDuplexXmlAdapter extends XmlAdapter<String, String> {

    @Override
    public String unmarshal(String value) throws Exception {
        return value;
    }

    @Override
    public String marshal(String value) throws Exception {
        //ignore marshall so you have half duplex
        return null;
    }

}

and then use it this way:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User {

    String login;

    @XmlJavaTypeAdapter(HalfDuplexXmlAdapter.class)
    String password;

    // getters and setters
}

So this way, password send from client to server is serialized and deserialized, but when User is sent from server to client password is not serialized and/or deserialized, and so not sent (null).

于 2015-12-23T21:00:21.133 回答