12

デフォルトでは、Gson はフィールドをシリアル化の基礎として使用します。代わりにアクセサーを使用する方法はありますか?

4

2 に答える 2

8

Gson の開発者は、この機能を追加するという要求に動揺したことは一度もないと述べており、この機能のサポートを追加するために API を曖昧にすることを心配していました。

この機能を追加する 1 つの方法は、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 を介して登録できます。おそらく、ランタイム アノテーションの存在を確認します。

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

これは、gson インスタンスを作成するときに通常どおり指定できます。

new GsonBuilder().registerTypeAdapterFactory(new TypeFactory()).create();
于 2012-07-08T17:39:24.733 に答える
2

注: 私はEclipseLink JAXB(MOXy)のリーダーであり、JAXB(JSR-222)エキスパートグループのメンバーです。

Gsonに必要な処理を実行させることができない場合は、MOXyのネイティブJSONバインディングを使用してこれを実現する方法を以下に示します。MOXyは、他のJAXB実装と同様に、デフォルトでプロパティ(パブリック)アクセスを使用します。を使用してフィールドアクセスを構成できます@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

MOXyをJAXBプロバイダーとして構成するには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 / Output

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

詳細については

于 2012-07-08T19:32:57.097 に答える