2012-07-08 10 views

答えて

7

Gson sayの開発者は、この機能を追加する要求に揺さぶられたことは決してありませんでした。また、これをサポートするためにAPIを怪しくすることを心配していました。この機能を追加する

一つの方法は、(私は危ないコードをお詫び申し上げますが、これは原理を実証)TypeAdapterを使用することです:

import java.io.IOException; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

import com.google.common.base.CaseFormat; 
import com.google.gson.Gson; 
import com.google.gson.TypeAdapter; 
import com.google.gson.reflect.TypeToken; 
import com.google.gson.stream.JsonReader; 
import com.google.gson.stream.JsonWriter; 

public class AccessorBasedTypeAdaptor<T> extends TypeAdapter<T> { 

    private Gson gson; 

    public AccessorBasedTypeAdaptor(Gson gson) { 
    this.gson = gson; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public void write(JsonWriter out, T value) throws IOException { 
    out.beginObject(); 
    for (Method method : value.getClass().getMethods()) { 
     boolean nonBooleanAccessor = method.getName().startsWith("get"); 
     boolean booleanAccessor = method.getName().startsWith("is"); 
     if ((nonBooleanAccessor || booleanAccessor) && !method.getName().equals("getClass") && method.getParameterTypes().length == 0) { 
     try { 
      String name = method.getName().substring(nonBooleanAccessor ? 3 : 2); 
      name = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name); 
      Object returnValue = method.invoke(value); 
      if(returnValue != null) { 
      TypeToken<?> token = TypeToken.get(returnValue.getClass()); 
      TypeAdapter adapter = gson.getAdapter(token); 
      out.name(name); 
      adapter.write(out, returnValue); 
      } 
     } catch (Exception e) { 
      throw new ConfigurationException("problem writing json: ", e); 
     } 
     } 
    } 
    out.endObject(); 
    } 

    @Override 
    public T read(JsonReader in) throws IOException { 
    throw new UnsupportedOperationException("Only supports writes."); 
    } 
} 

あなたは指定された型のノーマルタイプのアダプタとしてこれを登録することができますかTypeAdapterfactoryて - おそらくランタイム注釈の存在をチェック:あなたのgsonインスタンスを作成する際に

public class TypeFactory implements TypeAdapterFactory { 

    @SuppressWarnings("unchecked") 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) { 
    Class<? super T> t = type.getRawType(); 
    if(t.isAnnotationPresent(UseAccessor.class)) { 
    return (TypeAdapter<T>) new AccessorBasedTypeAdaptor(gson); 
    } 
    return null; 
    } 

これは通常のように指定することができます。

new GsonBuilder().registerTypeAdapterFactory(new TypeFactory()).create(); 
+0

華麗な例。実際にこれを進めるなら、おそらくTypeAdapterFactory.create()のメソッドを探してキャッシュすることで、少し効率的にすることができます。 –

+0

@ plasma147ありがとう! UseAccessorアノテーションは次のとおりです。 '@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface UseAccessor { } –

+0

ありがとうございました!神様が私にこれ以上のトラブルをもたらしました。あなたが書いたものの正式な例がないのはなぜですか? UseAccessor-annotationのSmokyに感謝します。Javaにはかなり新しいので、注釈部分を理解しようとするとさらに問題が発生します。 – Whyser

2

注:私はEclipseLink JAXB (MOXy)リードとJAXB (JSR-222)専門家グループのメンバーです。

Gsonに必要な操作をさせることができない場合は、MOXyのネイティブJSONバインディングを使用してこれを実行する方法を以下に示します。 JAXB実装のようなMOXyは、デフォルトでプロパティ(パブリック)アクセスを使用します。 @XmlAccessorType(XmlAccessType.FIELD)を使用してフィールドアクセスを設定できます。以下は例です:

カスタマー

package forum11385214; 

public class Customer { 

    private String foo; 
    private Address bar; 

    public String getName() { 
     return foo; 
    } 

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

    public Address getAddress() { 
     return bar; 
    } 

    public void setAddress(Address address) { 
     this.bar = address; 
    } 

} 

住所

package forum11385214; 

public class Address { 

    private String foo; 

    public String getStreet() { 
     return foo; 
    } 

    public void setStreet(String street) { 
     this.foo = street; 
    } 

} 

jaxb.properties

あなたのJAXBプロバイダとしてMOXYを設定するには、追加する必要がありますファイルjaxb.propertiesをドメインモデルと同じパッケージに入れ、次のエントリを入力します(http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.htmlを参照)。詳細情報

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

デモ

package forum11385214; 

import java.util.*; 
import javax.xml.bind.*; 
import javax.xml.transform.stream.StreamSource; 
import org.eclipse.persistence.jaxb.JAXBContextProperties; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     Map<String, Object> properties = new HashMap<String, Object>(2); 
     properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json"); 
     properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false); 
     JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     StreamSource json = new StreamSource("src/forum11385214/input.json"); 
     Customer customer = (Customer) unmarshaller.unmarshal(json, Customer.class).getValue(); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(customer, System.out); 
    } 

} 

input.json /出力

{ 
    "name" : "Jane Doe", 
    "address" : { 
     "street" : "1 Any Street" 
    } 
} 

関連する問題