Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You can do without manually unproxying everything by using a custom <code>TypeAdapter</code>. Something along these lines:</p> <pre><code>/** * This TypeAdapter unproxies Hibernate proxied objects, and serializes them * through the registered (or default) TypeAdapter of the base class. */ public class HibernateProxyTypeAdapter extends TypeAdapter&lt;HibernateProxy&gt; { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @Override @SuppressWarnings("unchecked") public &lt;T&gt; TypeAdapter&lt;T&gt; create(Gson gson, TypeToken&lt;T&gt; type) { return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter&lt;T&gt;) new HibernateProxyTypeAdapter(gson) : null); } }; private final Gson context; private HibernateProxyTypeAdapter(Gson context) { this.context = context; } @Override public HibernateProxy read(JsonReader in) throws IOException { throw new UnsupportedOperationException("Not supported"); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void write(JsonWriter out, HibernateProxy value) throws IOException { if (value == null) { out.nullValue(); return; } // Retrieve the original (not proxy) class Class&lt;?&gt; baseType = Hibernate.getClass(value); // Get the TypeAdapter of the original class, to delegate the serialization TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType)); // Get a filled instance of the original class Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer() .getImplementation(); // Serialize the value delegate.write(out, unproxiedValue); } } </code></pre> <p>To use it you must first register it:</p> <pre><code>GsonBuilder b = new GsonBuilder(); ... b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY); ... Gson gson = b.create(); </code></pre> <p>Notice that this will recursively initialize every proxy you have in the object hierarchy; since however you have to serialize the whole data, you should have done that anyway.</p> <p><strong>How does this work?</strong></p> <p>GSON contains a number of <code>TypeAdapterFactory</code> implementations, for various types (primitive types, common types like <code>String</code> or <code>Date</code>, lists, arrays...). Each factory is asked if it is able to serialize a certain Java type (the parameter to <code>create</code> is a <code>TypeToken</code> instead of a <code>Class</code> in order to capture possible information about generic types, which <code>Class</code> does not have). If the factory is able to serialize/deserialize a type, it responds with a <code>TypeAdapter</code> instance; otherwise it responds with <code>null</code>.</p> <p><code>HibernateProxyTypeAdapter.FACTORY</code> verifies whether <em>type</em> implements <code>HibernateProxy</code>; in that case, it returns an instance of <code>HibernateProxyTypeAdapter</code> for serialization. The <code>write</code> method is called when an actual object has to be serialized; the adapter extracts the original type of the underlying object, and asks GSON for the standard <code>TypeAdapter</code> for the original type, which generally is a <code>ReflectiveTypeAdapter</code>.</p> <p>Then it retrieves an instance of the original class, instead of directly using the proxy. This is necessary because <code>ReflectiveTypeAdapter</code> accesses <a href="http://code.google.com/p/google-gson/issues/detail?id=5" rel="noreferrer">directly to fields</a>, instead of using getters; accessing to the fields of a proxied object does not work, and is a classical <a href="http://blog.xebia.com/2008/03/08/advanced-hibernate-proxy-pitfalls/" rel="noreferrer">Hibernate pitfall</a>.</p> <p>As a possible performance improvement, the delegate <code>TypeAdapter</code> should be acquired in the <code>create</code> method. I found out that calling <code>getSuperclass()</code> on the proxy <code>Class</code> appears to yield the original base class. The code can then become:</p> <pre><code>public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @Override @SuppressWarnings("unchecked") public &lt;T&gt; TypeAdapter&lt;T&gt; create(Gson gson, TypeToken&lt;T&gt; type) { return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter&lt;T&gt;) new HibernateProxyTypeAdapter((TypeAdapter)gson.getAdapter(TypeToken.get(type.getRawType().getSuperclass()))) : null); } }; private final TypeAdapter&lt;Object&gt; delegate; private HibernateProxyTypeAdapter(TypeAdapter&lt;Object&gt; delegate) { this.delegate = delegate; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void write(JsonWriter out, HibernateProxy value) throws IOException { if (value == null) { out.nullValue(); return; } delegate.write(out, ((HibernateProxy) value).getHibernateLazyInitializer() .getImplementation()); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload