Я вызываю этот метод, когда получаю ответ от внутреннего интерфейса Stripe. Когда я вызываю функцию fromJson, возвращаемое значение равно нулю. Я не вижу, что я делаю здесь не так? Я использую OkHttp для вызова API, и когда я записываю значение responseBody.string (), я получаю чистую строку JSON.

val gson = Gson()
val type: Type = object : TypeToken<Map<String, Any>?>() {}.type
val jsonMap: Map<String, Any> = gson.fromJson(responseBody.string(), type)

Вот ответ от API, но удалили конфиденциальные данные:

{ 
   "id":"cus_XXXXXXXXXXX",
   "object":"customer",
   "address":null,
   "balance":0,
   "created":1575563900,
   "currency":null,
   "default_source":null,
   "delinquent":false,
   "description":null,
   "discount":null,
   "email":"XXXXXXXXXXXXX",
   "invoice_prefix":"XXXXXXXX",
   "invoice_settings":{ 
      "custom_fields":null,
      "default_payment_method":null,
      "footer":null
   },
   "livemode":true,
   "metadata":{ 

   },
   "name":null,
   "phone":null,
   "preferred_locales":[ 

   ],
   "shipping":null,
   "sources":{ 
      "object":"list",
      "data":[ 

      ],
      "has_more":false,
      "total_count":0,
      "url":"/v1/customers/cus_XXXXXXXXXXX/sources"
   },
   "subscriptions":{ 
      "object":"list",
      "data":[ 

      ],
      "has_more":false,
      "total_count":0,
      "url":"/v1/customers/cus_XXXXXXXXXXX/subscriptions"
   },
   "tax_exempt":"none",
   "tax_ids":{ 
      "object":"list",
      "data":[ 

      ],
      "has_more":false,
      "total_count":0,
      "url":"/v1/customers/cus_XXXXXXXXXXX/tax_ids"
   },
   "tax_info":null,
   "tax_info_verification":null
}

Изменить: я теперь создал класс данных для ответа, однако при изменении моего кода я все еще получаю ноль в качестве объекта клиента.

data class StripeCustomer(
    @SerializedName("id") val id: String?,
    @SerializedName("object") val obj: String?,
    @SerializedName("address") val address: String?,
    @SerializedName("balance") val balance: Any,
    @SerializedName("created") val created: Any,
    @SerializedName("currency") val currency: Any,
    @SerializedName("default_source") val defaultSource: Any,
    @SerializedName("delinquent") val delinquent: Any,
    @SerializedName("description") val description: Any,
    @SerializedName("discount") val discount: Any,
    @SerializedName("email") val email: String?,
    @SerializedName("invoice_prefix") val invoicePrefix: Any,
    @SerializedName("invoice_settings") val invoiceSettings: Any,
    @SerializedName("livemode") val livemode: Any,
    @SerializedName("metadata") val metadata: Any,
    @SerializedName("name") val name: Any,
    @SerializedName("phone") val phone: Any,
    @SerializedName("preferred_locales") val preferredLocales: Any,
    @SerializedName("shipping") val shipping: Any,
    @SerializedName("sources") val sources: Any,
    @SerializedName("subscriptions") val subscriptions: Any,
    @SerializedName("tax_exempt") val taxExempt: Any,
    @SerializedName("tax_ids") val taxIds: Any,
    @SerializedName("tax_info") val taxInfo: Any,
    @SerializedName("tax_info_verification") val taxInfoVerification: Any
)

Отредактированные функции Gson:

val gson = Gson()
val customerObject = gson.fromJson(responseBody.string(), StripeCustomer::class.java)
val customerId = customerObject.id ?: return@subscribe

Код API:

Я использую Пример проекта Stripe, как показано ниже:

interface BackendApi {

    @FormUrlEncoded
    @POST("ephemeral_keys")
    fun createEphemeralKey(@FieldMap apiVersionMap: HashMap<String, String>): Observable<ResponseBody>

    @FormUrlEncoded
    @POST("create_customer")
    fun createCustomer(@FieldMap emailMap: HashMap<String, String>): Observable<ResponseBody>

    @FormUrlEncoded
    @POST("create_payment_intent")
    fun createPaymentIntent(@FieldMap params: MutableMap<String, Any>): Observable<ResponseBody>

    @FormUrlEncoded
    @POST("create_setup_intent")
    fun createSetupIntent(@FieldMap params: HashMap<String, Any>): Observable<ResponseBody>
}

internal class BackendApiFactory internal constructor(private val backendUrl: String) {

    constructor(context: Context) : this(Settings(context).backendUrl)

    fun create(): BackendApi {
        // Set your desired log level. Use Level.BODY for debugging errors.
        // Adding Rx so the calls can be Observable, and adding a Gson converter with
        // leniency to make parsing the results simple.
        val logging = HttpLoggingInterceptor()
            .setLevel(HttpLoggingInterceptor.Level.BODY)

        val httpClient = OkHttpClient.Builder()
            .connectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
            .readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
            .addInterceptor(logging)
            .addNetworkInterceptor(StethoInterceptor())
            .build()

        val gson = GsonBuilder()
            .setLenient()
            .create()

        return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(backendUrl)
            .client(httpClient)
            .build()
            .create(BackendApi::class.java)
    }

    private companion object {
        private const val TIMEOUT_SECONDS = 15L
    }
}

--------- ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ ---------

compositeDisposable.add(backendApi.createCustomer(hashMapOf("email" to currentUserEmail))
                            .subscribeOn(Schedulers.io())
                            .subscribe({ stripeCustomer ->

                                val customerId = stripeCustomer.id ?: return@subscribe
                                println(customerId)

...

Выше показан API, вызываемый с предложенными изменениями.

Ниже представлен измененный интерфейс BackendApi.

@FormUrlEncoded
    @POST("create_customer")
    fun createCustomer(@FieldMap emailMap: HashMap<String, String>): Observable<StripeCustomer>

Возвращает следующую трассировку стека:

ava.lang.IllegalArgumentException: Unable to create converter for class com.fisherassociates.mt18academy.Controllers.StripeCheckout.StripeCustomer
        for method BackendApi.createCustomer
        at retrofit2.Utils.methodError(Utils.java:52)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:115)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy0.createCustomer(Unknown Source)
        at com.fisherassociates.mt18academy.Controllers.StripeCheckout.EphemeralKeyProvider$createEphemeralKey$1.onSuccess(EphemeralKeyProvider.kt:45)
        at com.fisherassociates.mt18academy.Controllers.StripeCheckout.EphemeralKeyProvider$createEphemeralKey$1.onSuccess(EphemeralKeyProvider.kt:21)
        at com.google.android.gms.tasks.zzn.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.IllegalArgumentException: class com.fisherassociates.mt18academy.Controllers.StripeCheckout.StripeCustomer declares multiple JSON fields named invoice_prefix
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:172)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
        at com.google.gson.Gson.getAdapter(Gson.java:458)
        at retrofit2.converter.gson.GsonConverterFactory.responseBodyConverter(GsonConverterFactory.java:64)
        at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
        at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:113)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82) 
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37) 
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170) 
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149) 
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006) 
        at $Proxy0.createCustomer(Unknown Source) 
        at com.fisherassociates.mt18academy.Controllers.StripeCheckout.EphemeralKeyProvider$createEphemeralKey$1.onSuccess(EphemeralKeyProvider.kt:45) 
        at com.fisherassociates.mt18academy.Controllers.StripeCheckout.EphemeralKeyProvider$createEphemeralKey$1.onSuccess(EphemeralKeyProvider.kt:21) 
        at com.google.android.gms.tasks.zzn.run(Unknown Source:4) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
-1
Elliot Fisher 5 Дек 2019 в 19:51
Сделайте модель json из api. используйте плагины json для java-моделей в студии Android для создания моделей. Вместо передачи карты в Type Token передайте эту модель. Ваши данные будут помещены в модель
 – 
Ashish Pardhiye
5 Дек 2019 в 20:15
Я не понимаю, почему вы пытаетесь поместить свою модель на карту. Вы должны определить класс данных с аннотациями GSON для отображения вашей модели.
 – 
Nicola Gallazzi
5 Дек 2019 в 20:25

1 ответ

Вы должны создать data модель для анализа данных из json`.

Сначала создайте модель, как показано ниже:

data class StripeCustomer(
    @SerializedName("custom_fields")
    var invoiceSettings: InvoiceSettings,
    @SerializedName("preferred_locales")
    var preferredLocales: List<String>,
    @SerializedName("sources")
    var sources: Sources,
    @SerializedName("subscriptions")
    var subscriptions: Sources,
    @SerializedName("tax_ids")
    var taxIds: Sources,
    @SerializedName("tax_info")
    var taxInfo: String?,
    @SerializedName("tax_info_verification")
    var taxInfoVerification: String?,
    @SerializedName("tax_exempt")
    var taxExempt: String?,
    @SerializedName("shipping")
    var shipping: String?,
    @SerializedName("phone")
    var phone: String?,
    @SerializedName("invoice_prefix")
    var name: String?,
    @SerializedName("livemode")
    var livemode: Boolean?,
    @SerializedName("invoice_prefix")
    var invoicePrefix: String?,
    @SerializedName("email")
    var email: String?,
    @SerializedName("discount")
    var discount: String?,
    @SerializedName("description")
    var description: String?,
    @SerializedName("delinquent")
    var delinquent: Boolean?,
    @SerializedName("default_source")
    var defaultSource: String?,
    @SerializedName("currency")
    var currency: String?,
    @SerializedName("created")
    var created: Long?,
    @SerializedName("balance")
    var balance: Int?,
    @SerializedName("address")
    var address: String?,
    @SerializedName("object")
    var objectType: String?,
    @SerializedName("id")
    var id: String?
)

data class InvoiceSettings (
    @SerializedName("custom_fields")
    var customFields: String?,
    @SerializedName("default_payment_method")
    var defaultPaymentMethod: String?,
    @SerializedName("footer")
    var footer: String?
)

data class Sources (
    @SerializedName("object")
    var objects: String?,
    @SerializedName("data")
    var data: List<String>,
    @SerializedName("has_more")
    var hasMore: Boolean?,
    @SerializedName("total_count")
    var totalCount: Int?,
    @SerializedName("url")
    var url: String?
)

Затем преобразуйте json в StripeCustomer, как показано ниже:

val gson = Gson()
val stripeCustomer = gson.fromJson(responseBody.string(), StripeCustomer::class.java)
0
Md. Asaduzzaman 5 Дек 2019 в 20:54
Спасибо за ваш ответ, я изменил свой код на приведенные выше фрагменты, но я все еще получаю null?
 – 
Elliot Fisher
5 Дек 2019 в 21:06
Да, конечно, как упоминалось в редактировании, я уже использовал этот бэкэнд-код для приложения iOS, и он отлично работает, просто не знаю, почему функция gson продолжает возвращать нулевой объект!
 – 
Elliot Fisher
5 Дек 2019 в 21:17
Нет, мне нужна часть Android, которую вы использовали для получения данных с сервера.
 – 
Md. Asaduzzaman
5 Дек 2019 в 21:21
С помощью какого из них вы получаете эти данные?
 – 
Md. Asaduzzaman
5 Дек 2019 в 21:30
CreateCustomer - это то, о чем вы спрашиваете?
 – 
Elliot Fisher
5 Дек 2019 в 21:31