我有一个简单的枚举:
public enum Season {
    @Serializedname("0")
    AUTUMN,@Serializedname("1")
    SPRING;
}

从某个版本开始,GSON就能够解析这些枚举.为了确保,我这样做了:

final String s = gson.toJson(Season.AUTUMN);

它按我的预期工作.输出为“0”.所以,我尝试在我的Retrofit服务中使用它:

@GET("index.PHP?page[api]=test")
Observable<List<Month>> getMonths(@Query("season_lookup") Season season);
/*...some files later...*/
service.getMonths(Season.AUTUMN);

并且还添加了日志以确定其结果:

HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient httpClient = new OkHttpClient.Builder()
        .addInterceptor(httpLoggingInterceptor)
        .build();

但它失败了. @Query完全忽略@Serializedname并改为使用.toString(),所以日志显示为… / index.PHP?page [api] = test& season_lookup = AUTUMN.

我跟踪了Retrofit源代码并找到了带有行的文件RequestFactoryParser:

Converter<?,String> converter = 
    retrofit.stringConverter(parameterType,parameterannotations);
action = new RequestAction.Query<>(name,converter,encoded);

似乎,就像它根本不关心枚举一样.在这些行之前,它将rawParameterType.isArray()测试为数组或Iterable.class.isAssignableFrom(),仅此而已.

改造实例创建是:

retrofit = new Retrofit.Builder()
                .baseUrl(ApiConstants.API_ENDPOINT)
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

gson是GsonBuilder().create().我偷看了源代码,其中有枚举的预定义ENUM_TypeAdapters.ENUM_FACTORY,所以我保持原样.

问题是我该怎么办,以防止在我的枚举上使用toString()并使用@Serializedname?我将toString()用于其他目的.

解决方法

正如@DawidSzydło所提到的,我在Retrofit中误解了Gson的用法.它仅用于响应/请求解码/编码,但不用于@Query / @ Url / @ Path e.t.c.对于他们来说,Retrofit使用Converter.Factory将任何类型转换为String.
以下是将@Serializedname作为任何Enum的值传递给Retrofit服务时自动使用的代码.

转换器:

public class EnumRetrofitConverterFactory extends Converter.Factory {
    @Override
    public Converter<?,String> stringConverter(Type type,Annotation[] annotations,Retrofit retrofit) {
        Converter<?,String> converter = null;
        if (type instanceof Class && ((Class<?>)type).isEnum()) {
            converter = value -> EnumUtils.GetSerializednameValue((Enum) value);
        }
        return converter;
    }
}

EnumUtils:

public class EnumUtils {
    @Nullable
    static public <E extends Enum<E>> String GetSerializednameValue(E e) {
        String value = null;
        try {
            value = e.getClass().getField(e.name()).getAnnotation(Serializedname.class).value();
        } catch (NoSuchFieldException exception) {
            exception.printstacktrace();
        }
        return value;
    }
}

改造创造:

retrofit = new Retrofit.Builder()
        .baseUrl(ApiConstants.API_ENDPOINT)
        .client(httpClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addConverterFactory(new EnumRetrofitConverterFactory())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

08.18更新添加kotlin类似物:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val httpLoggingInterceptor = HttpLoggingInterceptor()
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        val httpClient = OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .build()

        val gson = GsonBuilder().create()

        val retrofit = Retrofit.Builder()
                .baseUrl(Api.ENDPOINT)
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addConverterFactory(EnumConverterFactory())
                .build()

        val service = retrofit.create(Api::class.java)
        service.getMonths(Season.AUTUMN).enqueue(object : Callback<List<String>> {
            override fun onFailure(call: Call<List<String>>?,t: Throwable?) {
                /* ignore */
            }

            override fun onResponse(call: Call<List<String>>?,response: Response<List<String>>?) {
                /* ignore */
            }
        })
    }
}

class EnumConverterFactory : Converter.Factory() {
    override fun stringConverter(type: Type?,annotations: Array<out Annotation>?,retrofit: Retrofit?): Converter<*,String>? {
        if (type is Class<*> && type.isEnum) {
            return Converter<Any?,String> { value -> getSerializednameValue(value as Enum<*>) }
        }
        return null
    }
}

fun <E : Enum<*>> getSerializednameValue(e: E): String {
    try {
        return e.javaClass.getField(e.name).getAnnotation(Serializedname::class.java).value
    } catch (exception: NoSuchFieldException) {
        exception.printstacktrace()
    }

    return ""
}

enum class Season {
    @Serializedname("0")
    AUTUMN,@Serializedname("1")
    SPRING
}

interface Api {
    @GET("index.PHP?page[api]=test")
    fun getMonths(@Query("season_lookup") season: Season): Call<List<String>>

    companion object {
        const val ENDPOINT = "http://127.0.0.1"
    }
}

在日志中,您将看到:

D/OkHttp: --> GET http://127.0.0.1/index.PHP?page[api]=test&season_lookup=0 
D/OkHttp: --> END GET 
D/OkHttp: <-- HTTP Failed: java.net.ConnectException: Failed to connect to /127.0.0.1:80

使用的依赖项是:

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

android – 如何通过Retrofit在@Query中传递自定义枚举?的更多相关文章

  1. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  3. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  4. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  5. xamarin – 崩溃在AccountStore.Create().保存(e.Account,“);

    在Xamarin.Forms示例TodoAwsAuth中https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/oauth/成功登录后,在aOnAuthenticationCompleted事件中,应用程序在尝试保存到Xamarin.Auth时崩溃错误说不能对钥匙串说期待着寻求帮助.解决方法看看你

  6. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  7. xcode – 错误“线程1:断点2.1”

    我正在研究RESTAPI管理器.这是一个错误,我无法解决它.我得到的错误在下面突出显示.当我打电话给这个班级获取资源时:我评论的线打印:Thread1:breakpoint2.1我需要修复错误的建议.任何建议都非常感谢解决方法您可能在不注意的情况下意外设置了断点.单击并拖动代表断路器外部断点的蓝色刻度线以将其擦除.

  8. ios – 更改导航栏标题swift中的字符间距

    类型的值有人可以帮我这个或建议一种不同的方式来改变swift中导航栏标题中的字符间距吗?解决方法您无法直接设置属性字符串.你可以通过替换titleView来做一个技巧

  9. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  10. ios – 如果我将自动释放的对象桥接到Core Foundation,我必须使用__bridge或__bridge_retained吗?

    ARC迁移工具遇到了这个问题:特别是,它不确定它是否应该执行__bridge或__bridge_retained.而我也是.-fileURLWithPath返回一个自动释放的对象,在这个地方我不是fileURL的所有者.但与此同时,该对象的保留计数至少为1.我敢打赌,这只能用__bridge来完成.解决方法您只想为此使用常规__bridge强制转换.仅当您想要管理强制转换CF对象的生命周期时,才会使用__bridge_retained.例如:所以__bridge_retained确实告诉编译器你有一个AR

随机推荐

  1. bluetooth-lowenergy – Altbeacon库无法在Android 5.0上运行

    昨天我在Nexus4上获得了Android5.0的更新,并且altbeacon库停止了检测信标.似乎在监视和测距时,didEnterRegion和didRangeBeaconsInRegion都没有被调用.即使RadiusNetworks的Locate应用程序现在表现不同,一旦检测到信标的值,它们就不再得到更新,并且通常看起来好像信标超出了范围.我注意到的一点是,现在在logcat中出现以下行“B

  2. android – react-native动态更改响应者

    我正在使用react-native进行Android开发.我有一个视图,如果用户长按,我想显示一个可以拖动的动画视图.我可以使用PanResponder实现这一点,它工作正常.但我想要做的是当用户长按时,用户应该能够继续相同的触摸/按下并拖动新显示的Animated.View.如果您熟悉Google云端硬盘应用,则它具有类似的功能.当用户长按列表中的任何项目时,它会显示可拖动的项目.用户可以直接拖

  3. android – 是否有可能通过使用与最初使用的证书不同的证书对其进行签名来发布更新的应用程序

    是否可以通过使用与最初使用的证书不同的证书进行签名来发布Android应用程序的更新?我知道当我们尝试将这样的构建上传到市场时,它通常会给出错误消息.但有没有任何出路,比如将其标记为主要版本,指定市场中的某个地方?解决方法不,你不能这样做.证书是一种工具,可确保您是首次上传应用程序的人.所以总是备份密钥库!

  4. 如何检测Android中是否存在麦克风?

    ..所以我想在让用户访问语音输入功能之前检测麦克风是否存在.如何检测设备上是否有麦克风.谢谢.解决方法AndroidAPI参考:hasSystemFeature

  5. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

  6. android – 获得一首歌的流派

    我如何阅读与歌曲相关的流派?我可以读这首歌,但是如何抓住这首歌的流派,它存放在哪里?解决方法检查此代码:

  7. android – 使用textShadow折叠工具栏

    我有一个折叠工具栏的问题,在展开状态我想在文本下面有一个模糊的阴影,我使用这段代码:用:我可以更改textColor,它可以工作,但阴影不起作用.我为阴影尝试了很多不同的值.是否可以为折叠文本投射阴影?

  8. android – 重用arm共享库

    我已经建立了armarm共享库.我有兴趣重用一个函数.我想调用该函数并获得返回值.有可能做这样的事吗?我没有任何头文件.我试过这个Android.mk,我把libtest.so放在/jni和/libs/armeabi,/lib/armeabi中.此时我的cpp文件编译,但现在是什么?我从objdump知道它的名字编辑:我试图用这个android.mk从hello-jni示例中添加prebuild库:它工作,但libtest.so相同的代码显示以下错误(启动时)libtest.so存在于libhello-j

  9. android – 为NumberPicker捕获键盘’Done’

    我有一个AlertDialog只有一些文本,一个NumberPicker,一个OK和一个取消.(我知道,这个对话框还没有做它应该保留暂停和恢复状态的事情.)我想在软键盘或其他IME上执行“完成”操作来关闭对话框,就像按下了“OK”一样,因为只有一个小部件可以编辑.看起来处理IME“Done”的最佳方法通常是在TextView上使用setonEditorActionListener.但我没有任何Te

  10. android – 想要在调用WebChromeClient#onCreateWindow时知道目标URL

    当我点击一个带有target=“_blank”属性的超链接时,会调用WebChromeClient#onCreateWindow,但我找不到新的窗口将打开的新方法?主页url是我唯一能知道的东西?我想根据目标网址更改应用行为.任何帮助表示赞赏,谢谢!

返回
顶部