使用Retrofit2解析XML。多个结果列表不起作用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Retrofit2解析XML。多个结果列表不起作用相关的知识,希望对你有一定的参考价值。
我正在使用Retrofit2 V2.6.0作为REST客户端来连接Goodreads的api。由于Goodreads的api返回XML格式的数据,因此我将SimpleXML V2.6.0用作转换器。我一直坚持将xml转换成POJO。奇怪的是,当结果是一本书时,它会很好地工作,但是在下面显示错误(最后一个代码片段)。 xml中的书表示为work。当结果包含一个work元素时,它将起作用并正确地转换XML,否则不会。
编辑开始
我发现,即使是具有两个结果的查询也可以很好地反序列化。四个结果调用相同的异常。尝试的查询如下。
1结果https://www.goodreads.com/search/index.xml?key=XXXX&q=tesfi
2结果https://www.goodreads.com/search/index.xml?key=XXXX&q=tesfu
4个结果https://www.goodreads.com/search/index.xml?key=XXXX&q=tesff
编辑结束
我只需要搜索api,下面是响应的链接和成绩单。
https://www.goodreads.com/search/index.xml?key=XXXXXXXX&q=test
<GoodreadsResponse>
<Request>
<authentication>true</authentication>
<key>
<![CDATA[ lO9hYshON1dw3N798dkCWg ]]>
</key>
<method>
<![CDATA[ search_index ]]>
</method>
</Request>
<search>
<query>
<![CDATA[ test ]]>
</query>
<results-start>1</results-start>
<results-end>20</results-end>
<total-results>132594</total-results>
<source>Goodreads</source>
<query-time-seconds>0.05</query-time-seconds>
<results>
<work> // treat this as book mentioned above, if only one of these exists the code works.
<id type="integer">14262366</id>
<books_count type="integer">55</books_count>
<ratings_count type="integer">115931</ratings_count>
<text_reviews_count type="integer">6845</text_reviews_count>
<original_publication_year type="integer">2011</original_publication_year>
<original_publication_month type="integer">5</original_publication_month>
<original_publication_day type="integer">12</original_publication_day>
<average_rating>3.94</average_rating>
<best_book type="Book">
<id type="integer">12391521</id>
<title>
The Psychopath Test: A Journey Through the Madness Industry
</title>
<author>
<id type="integer">1218</id>
<name>Jon Ronson</name>
</author>
<image_url>
https://i.gr- assets.com/images/S/compressed.photo.goodreads.com/books/1364166270l/12391521._SX98_.jpg
</image_url>
<small_image_url>
https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1364166270l/12391521._SX50_.jpg
</small_image_url>
</best_book>
</work>
<work>...</work>
</results>
</search>
</GoodreadsResponse>
下面是我的POJO代码:
@Root(name = "GoodreadsResponse", strict = false)
data class SearchResultsResponse @JvmOverloads constructor(
@field:Element(name = "Request") @param:Element(name = "Request") var Request: Request,
@field:Element(name = "search", required = false) @param:Element(name = "search", required = false) var search: SearchResults
)
@Root(strict = false, name = "Request")
data class Request @JvmOverloads constructor(
@field:Element(name = "method") @param:Element(name = "method") var method: String,
@field:Element(name = "key") @param:Element(name = "key") var key: String,
@field:Element(name = "authentication", required = false) @param:Element(name = "authentication", required = false) var authentication: String
)
@Root(strict = false, name = "search")
data class SearchResults @JvmOverloads constructor(
@field:Element(name = "query") @param:Element(name = "query") var query: String,
@field:Element(name = "results-start", required = false) @param:Element(name = "results-start", required = false) var start: Int,
@field:Element(name = "results-end", required = false) @param:Element(name = "results-end", required = false) var end: Int,
@field:Element(name = "total-results", required = false) @param:Element(name = "total-results", required = false) var total: Int,
@field:Element(name = "source", required = false) @param:Element(name = "source", required = false) var source: String,
@field:Element(name = "query-time-seconds", required = false) @param:Element(name = "query-time-seconds", required = false) var queryTimeSeconds: String,
// The below commented lines are other things I have tried
//@field:ElementList(entry = "results", required = false) @param:ElementList(entry = "results", required = false) var results: List<SearchResult>? = null
//@field:ElementList(name = "results", entry = "work", type = SearchResult::class, required = false) var results: List<SearchResult>? = null
//@field:ElementList(name = "results", inline = true, type = SearchResult::class, required = false) var results: List<SearchResult>? = null
//@field:ElementList(name = "results", inline = false, type = SearchResult::class, required = false) var results: List<SearchResult>? = null
@field:ElementList(name = "results", entry = "work", type = SearchResult::class, required = false) var results: List<SearchResult>? = null
)
@Root(strict = false, name = "work")
data class SearchResult @JvmOverloads constructor(
@field:Element(name = "id", required = false) @param:Element(name = "id", required = false) var id: Int,
@field:Element(name = "books_count", required = false) @param:Element(name = "books_count", required = false) var booksCount: Int,
@field:Element(name = "ratings_count", required = false) @param:Element(name = "ratings_count", required = false) var ratingsCount: Int,
@field:Element(name = "text_reviews_count", required = false) @param:Element(name = "text_reviews_count", required = false) var textReviewsCount: Int,
@field:Element(name = "original_publication_year", required = false) @param:Element(name = "original_publication_year", required = false) var originalPublicationYear: Int,
@field:Element(name = "original_publication_month", required = false) @param:Element(name = "original_publication_month", required = false) var originalPublicationMonth: Int,
@field:Element(name = "original_publication_day", required = false) @param:Element(name = "original_publication_day", required = false) var originalPublicationDay: Int,
@field:Element(name = "average_rating", required = false) @param:Element(name = "average_rating", required = false) var averageRating: Float,
@field:Element(name = "best_book", required = false) @param:Element(name = "best_book", required = false) val book: Book
)
@Root(name = "best_book", strict = false)
data class Book @JvmOverloads constructor(
@field:Element(name = "id") @param:Element(name = "id") var id: Int,
@field:Element(name = "title", required = false) @param:Element(name = "title", required = false) var title: String,
@field:Element(name = "image_url", required = false) @param:Element(name = "image_url", required = false) var imageUrl: String,
@field:Element(name = "small_image_url", required = false) @param:Element(name = "small_image_url", required = false) var smallImageUrl: String,
@field:Element(name = "author", required = false) @param:Element(name = "author", required = false) var author: Author
)
@Root(strict = false, name = "author")
data class Author @JvmOverloads constructor(
@field:Element(name = "id") @param:Element(name = "id") var id: Int,
@field:Element(name = "name", required = false) @param:Element(name = "name", required = false) var name: String
)
实现请求的类:这里我使用了多种方法来转换数据
@SuppressWarnings("deprecation")
class FeedController : Callback<SearchResultsResponse>
fun run(query: String)
val persister = Persister(AnnotationStrategy())
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(
RxJava2CallAdapterFactory.create())
.addConverterFactory(
SimpleXmlConverterFactory.create(persister))
/** Tried with:
* SimpleXmlConverterFactory.createNonStrict(persister)
* SimpleXmlConverterFactory.createNonStrict()
* SimpleXmlConverterFactory.create()
* **/
.baseUrl("https://www.goodreads.com/")
.build()
val goodreadsApiService = retrofit.create(GoodreadsApiService::class.java)
val call:Call<SearchResultsResponse> = goodreadsApiService.getResults("XXXXXXXXX", query)
call.enqueue(this)
override fun onResponse(call: Call<SearchResultsResponse>, response: Response<SearchResultsResponse>)
if (response.isSuccessful)
Log.d("RETROMESSAGE", response.body().toString())
else
Log.d("RETROMESSAGE", "Error: "+response.errorBody())
override fun onFailure(call: Call<SearchResultsResponse>, t: Throwable)
t.printStackTrace()
Caused by: org.simpleframework.xml.core.PersistenceException: Constructor not matched for class com.xxxx.goodreads.model.SearchResult
at org.simpleframework.xml.core.ClassInstantiator.getInstance(ClassInstantiator.java:112)
at org.simpleframework.xml.core.Composite$Injector.readInject(Composite.java:1458)
at org.simpleframework.xml.core.Composite$Injector.readInject(Composite.java:1458)
我怀疑这与ElementList注释有关,但是我尝试过,所以可能会有所不同,结果是我没有其他想法了。
[发现查询即使有两个记录也可以使用后,我意识到这不是返回结果数量的问题,而是返回结果的位置。
在SearchResults POJO中,我将originalPublicationYear
,originalPublicationMonth
和originalPublicationDay
设置为整数,当它们返回为空时,将关闭PersistenceException
。由于我不需要它们,因此通过删除它们来解决此问题。但是我相信,如果您仍然需要它们,可以通过其他方式解决。
以上是关于使用Retrofit2解析XML。多个结果列表不起作用的主要内容,如果未能解决你的问题,请参考以下文章