2012-03-23 30 views
3

我正在序列化的對象包含具有Enums鍵的映射。這些枚舉有一個變量。當我使用GSON對其進行序列化時,我希望生成的JSON具有Enum變量而不是默認的Enum名稱。我已經嘗試創建一個自定義序列化器並註冊它,但它沒有做到這一點。這是我的代碼。用於使用枚舉鍵映射的GSON自定義序列化器

控制器:

@Controller 
public class CheckoutClientController { 

@Autowired 
private Gson gson; 
@Autowired 
private RequestHelper requestHelper; 
@Autowired 
private SettingsReader settingsReader; 

@InitBinder 
public void initBinder(final WebDataBinder binder) { 
    binder.registerCustomEditor(CheckoutConfigurationDto.class, new JsonDeserializerPropertyEditor<CheckoutConfigurationDto>(gson, CheckoutConfigurationDto.class)); 
} 

/** 
* Handles requests to the Checkout Client page, which is the outer wrapper that includes the white label checkout (WLC) iframe. Sets up the configuration 
* data needed to pass to the WLC server. 
* 
* @return the model and view 
*/ 
@RequestMapping(value = "/checkout/checkout-client.ep", method = RequestMethod.GET) 
public ModelAndView showPage(HttpServletRequest request) { 
    CheckoutClientConfigurationDto checkoutClientConfig = new CheckoutClientConfigurationDto(); 

    StringBuilder host = new StringBuilder(); 
    host.append(request.getScheme()).append("://"); 
    host.append(request.getServerName()); 
    host.append(":").append(request.getServerPort()); 

    checkoutClientConfig.setWlcHost(host.toString()); 
    checkoutClientConfig.setClientId("clientId"); 
    checkoutClientConfig.setAppId("appId"); 
    checkoutClientConfig.setId("wlc-widget"); 

    Map<CheckoutClientConfigurationOption, Boolean> options = checkoutClientConfig.getOptions(); 

    options.put(CheckoutClientConfigurationOption.SHOW_ORDER_CONFIRMATION, 
      Boolean.valueOf(this.settingsReader.getSettingValue(SettingsConstants.SHOW_ORDER_CONFIRMATION).getValue())); 
    options.put(CheckoutClientConfigurationOption.REMOVE_CART_ITEMS, 
      Boolean.valueOf(this.settingsReader.getSettingValue(SettingsConstants.REMOVE_CART_ITEMS).getValue())); 

    return new ModelAndView(ViewConstants.CHECKOUT_CLIENT_TEMPLATE_PATH, "checkoutClientConfig", gson.toJson(checkoutClientConfig)); 
} 
} 

CheckoutClientConfigurationDto(減去所有的樣板getter/setter方法):

public class CheckoutClientConfigurationDto implements Dto { 

private String wlcHost; 

private String clientId; 

private String appId; 

private String id; 

private Map<CheckoutClientConfigurationOption, Boolean> options; 

public CheckoutClientConfigurationDto() { 
    products = new ArrayList<ProductDto>(); 
    options = new HashMap<CheckoutClientConfigurationOption, Boolean>(); 
} 

public Map<CheckoutClientConfigurationOption, Boolean> getOptions() { 
    return options; 
} 

public void setOptions(final Map<CheckoutClientConfigurationOption, Boolean> options) { 
    this.options = options; 
} 
} 

CheckoutClientConfigurationOption:

public enum CheckoutClientConfigurationOption { 

SHOW_SAVED_ADDRESSES("showSavedAddresses", true), 
SHOW_CART_SUMMARY("showCartSummary", true), 
REMOVE_CART_ITEMS("removeCartItems", true), 
SHOW_DISCOUNT_FIELD("showDiscountField", true), 
SHOW_VAT_CODE("showVatCode", true), 
SHOW_ORDER_CONFIRMATION("showOrderConfirmation", true), 
SHOW_CANCEL_BUTTON("showCancelButton", false), 
SINGLE_PAGE_CHECKOUT("singlePageCheckout", false), 
SEND_ORDER_CONFIRMATION_EMAIL("sendOrderConfirmationEmail", true), 
SEND_SHIPPING_CONFIRMATION_EMAIL("sendShippingConfirmationEmail", true); 

private String optionName; 

private boolean defaultValue; 

private CheckoutClientConfigurationOption(final String optionName, final boolean defaultValue) { 
    this.optionName = optionName; 
    this.defaultValue = defaultValue; 
} 

public boolean getDefautValue() { 
    return defaultValue; 
} 

public String getOptionName() { 
    return optionName; 
} 
} 

我定製GSON串行:

public class CheckoutClientConfigurationOptionGsonSerializer implements JsonSerializer<CheckoutClientConfigurationOption> { 

@Override 
public JsonElement serialize(CheckoutClientConfigurationOption src, Type typeOfSrc, JsonSerializationContext context) { 
    return new JsonPrimitive(src.getOptionName()); 
} 

} 

我定製GSON配置者:

public class GsonConfigurer { 

private Map<Class<?>, Object> typeAdapterMap; 

public Gson create() { 
    final GsonBuilder gsonBuilder = new GsonBuilder(); 

    for (final Entry<Class<?>, Object> typeAdapterMapping : typeAdapterMap.entrySet()) { 
     gsonBuilder.registerTypeAdapter(typeAdapterMapping.getKey(), typeAdapterMapping.getValue()); 
    } 

    return gsonBuilder.create(); 
} 

protected Map<Class<?>, Object> getTypeAdapterMap() { 
    return typeAdapterMap; 
} 

public void setTypeAdapterMap(final Map<Class<?>, Object> typeAdapterMap) { 
    this.typeAdapterMap = typeAdapterMap; 
} 

} 

的XML:

<bean id="gsonConfigurer" class="com.sfweb.gson.GsonConfigurer"> 
    <property name="typeAdapterMap"> 
     <util:map key-type="java.lang.Class"> 
      <entry key="com.sfweb.dto.CheckoutConfigurationOption"> 
       <bean class="com.sfweb.dto.deserializer.CheckoutConfigurationOptionGsonDeserializer" /> 
      </entry> 
      <entry key="com.sfweb.dto.CheckoutClientConfigurationOption"> 
       <bean class="com.sfweb.dto.serializer.CheckoutClientConfigurationOptionGsonSerializer" /> 
      </entry> 
     </util:map> 
    </property> 
</bean> 

<bean class="com.google.gson.Gson" factory-bean="gsonConfigurer" factory-method="create" /> 

我也有一個自定義解串器,你可以在XML中看到。這個工作沒有問題。我運行在調試模式下,並且CheckoutClientConfigurationOptionGsonSerializer中的行不會被觸發。我檢查了我調用json()的gson對象中有自定義序列化器。所以我不確定問題是什麼。我有一種感覺,我只是缺少一塊。

我想要得到的JSON說「showSavedAddresses」,而是說「SHOW_SAVED_ADDRESSES」。先謝謝您的幫助!

+0

我遇到同樣的問題。我的猜測是,因爲JS對象上的鍵最終必須是字符串,所以它只是運行'value.toString()'而不是首選的'value.toJsonAndRunCustomSerializer()。toString()'。 – jpsimons 2013-03-22 23:50:05

回答

0

你會很好地服務於研究TypeAdapterFactory's documentation。它包括一個將枚舉設置爲小寫的例子;您可以根據自己的需要進行修改。

public class LowercaseEnumTypeAdapterFactory implements TypeAdapter.Factory { 
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
    Class<T> rawType = (Class<T>) type.getRawType(); 
    if (!rawType.isEnum()) { 
     return null; 
    } 

    final Map<String, T> lowercaseToConstant = new HashMap<String, T>(); 
    for (T constant : rawType.getEnumConstants()) { 
     lowercaseToConstant.put(toLowercase(constant), constant); 
    } 

    return new TypeAdapter<T>() { 
     public void write(JsonWriter out, T value) throws IOException { 
     if (value == null) { 
      out.nullValue(); 
     } else { 
      out.value(toLowercase(value)); 
     } 
     } 

     public T read(JsonReader reader) throws IOException { 
     if (reader.peek() == JsonToken.NULL) { 
      reader.nextNull(); 
      return null; 
     } else { 
      return lowercaseToConstant.get(reader.nextString()); 
     } 
     } 
    }; 
    } 

    private String toLowercase(Object o) { 
    return o.toString().toLowerCase(Locale.US); 
    } 
} 
+0

我試過這條路線,我幾乎得到了相同的結果。我發現問題在於,我的枚舉被嵌入爲我的映射的鍵。它將枚舉序列化爲小寫且沒有問題,如果我將它作爲DTO中的單個變量使用,但是當我將它用作映射的鍵​​時,它不會調用我的自定義序列化器或我的自定義類型適配器。 – 2012-03-25 23:38:32

2

閱讀該文檔上GsonBuilder#enableComplexMapKeySerialization我看到這一點:

地圖系列化的默認實現的關鍵

使用的toString(),所以它不上運行您TypeAdapter映射鍵默認情況下。我試着簡單地調用這個方法,並讓我的枚舉顯示爲數字字符串。