What's the right Scala and Java collection combination to use with nested JAXB? - scala

Working with JAXB, the standard way of dealing with a list of "nested" resource representations (e.g. <products><product>X</product><product>Y</product></products> is to create a wrapper object, which in Java might look like this (borrowed from Jhopify):
#XmlType(name = "")
#XmlRootElement(name = "products")
public class ProductList {
List<Product> products = new ArrayList<Product>();
#XmlElement(name = "product", required = true)
public List<Product> getProducts() { return products; }
public void setProducts(List<Product> products) { this.products = products; }
}
However I'm struggling to determine exactly which collection objects to use when translating this to Scala. There's a good introductory post to doing this on the Mostly Blather blog, which uses a Scala Iterable implicitly converted (using JavaConversions) to and from a JCollection.
This works great for marshalling a JAXB class to XML but unfortunately when unmarshalling this throws UnsupportedOperationException on each add attempt. Based on the last paragraph on this Scala documentation page it looks like this happens because Java does not distinguish between mutable and immutable collections in their type.
To deal with the unmarshalling, I've tried an alternative approach specifically using mutable objects:
#XmlType(name = "")
#XmlRootElement(name = "products")
class ProductList {
private var products: Buffer[Product] = new ArrayBuffer[Product]
#XmlElement(name = "product", required = true)
def getProducts: JList[Product] = products
def setProducts(products: JList[Product]) {
this.products = products
}
}
But unfortunately with this approach, unmarshalling gives me an exception:
java.lang.NoSuchMethodError: ProductList.getProducts()Ljava/util/Collection;
Edit: as per Travis' request, here is my unmarshalling code:
val jaxbContext = JAXBContext.newInstance(ProductList.getClass())
val unmarshaller = jaxbContext.createUnmarshaller()
val root = unmarshaller.unmarshal(new StreamSource(new StringReader(responseString)), ProductList.getClass())
val r = root.getValue().asInstanceOf[ProductList]
val representations = r.getProducts.asScala.toList // Uses scalaj
So I'm a bit stumped... I've looked at scalaj's available conversions too but nothing obvious jumps out. Any help much appreciated!

Could you post your unmarshalling code? I've done something similar with JAXB from Scala, and what you have looks like it should work. Here's a complete working example:
import javax.xml.bind.annotation._
class Thing {
#scala.reflect.BeanProperty var name: String = _
}
#XmlRootElement(name = "things")
class Things {
import scala.collection.JavaConversions._
import scala.collection.mutable.Buffer
private var things: Buffer[Thing] = Buffer[Thing]()
#XmlElement(name = "thing", required = true)
def getThings: java.util.List[Thing] = this.things
def setThings(things: java.util.List[Thing]) {
this.things = things
}
}
I'll write the test code in Scala as well, but it would work identically in Java.
object Things {
import java.io.StringReader
import java.io.StringWriter
import javax.xml.bind.JAXBContext
def main(args: Array[String]) {
val thing1 = new Thing
val thing2 = new Thing
thing1.setName("Thing 1")
thing2.setName("Thing 2")
val list: java.util.List[Thing] = new java.util.ArrayList[Thing]
list.add(thing1)
list.add(thing2)
val things = new Things
things.setThings(list)
val context = JAXBContext.newInstance(classOf[Things])
val writer = new StringWriter
context.createMarshaller.marshal(things, writer)
println(writer.toString)
val readThings = context.createUnmarshaller().unmarshal(
new StringReader(writer.toString)
).asInstanceOf[Things]
println("Size: " + readThings.getThings.size)
println("Name of first: " + readThings.getThings.get(0).getName)
}
}
This compiles and produces the output you'd expect.

Related

Kotlin: How to display data?

I'm working on this app that shows popular movies. I got this Log: "/results: retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall#9daee07". I got everything done but I don't get any result in my RecyclerView. I don't know how to display data that I fetched. This is url: http://api.themoviedb.org/3/movie/popular?api_key=api_key_goes_here.
MovieResponse.kt
data class MovieResponse(
val page: Int,
val results: List<Movie>,
#SerializedName("total_pages") val totalPages: Int,
#SerializedName("total_results") val totalResults: Int
)
Movie.kt
data class Movie(
val id: Int,
val overview: String,
val popularity: Double,
#SerializedName("poster_path") val posterPath: String,
#SerializedName("release_date") val releaseDate: String,
val title: String,
#SerializedName("vote_average") val voteAverage: Double
)
MainAdapter.kt
package com.example.sloomena.ui
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.sloomena.R
import com.example.sloomena.data.MovieResponse
import kotlinx.android.synthetic.main.movie_details_row.view.*
class MainAdapter: RecyclerView.Adapter<CustomHolder>(){
val movies: MutableList<MovieResponse> = mutableListOf()
fun refreshData(newResults: List<MovieResponse>) {
movies.clear()
movies.addAll(newResults)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: CustomHolder, position: Int) {
holder.bind(movies.get(position))
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.movie_details_row, parent, false)
return CustomHolder(cellForRow)
}
}
class CustomHolder(itemView: View): RecyclerView.ViewHolder(itemView){
fun bind(movie: MovieResponse) {
itemView.title.text = movie.results.toString()
}
}
thmdbAPI.kt
interface tmdbAPI {
#GET("movie/popular")
fun getPopularMovies(
#Query("api_key") api_key: String
): Call<List<MovieResponse>>
}
Networking.kt
const val BASE_URL = "http://api.themoviedb.org/3/"
object Networking{
val showSearchService: tmdbAPI = Builder()
.addConverterFactory(ConverterFactory.converterFactory)
.client(HttpClient.client)
.baseUrl(BASE_URL)
.build()
.create(tmdbAPI::class.java)
}
object ConverterFactory{
val converterFactory = GsonConverterFactory.create()
}
object HttpClient{
val client = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build()
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpUi()
findMovies()
}
private fun setUpUi() {
id_recyclerview.layoutManager = LinearLayoutManager(this)
id_recyclerview.adapter = MainAdapter()
}
private fun findMovies() {
GlobalScope.launch(Dispatchers.Main) {
val results = Networking.showSearchService.getPopularMovies("api_key_goes_here")
Log.d("results", results.toString())
}}
}
In my movie_details_row.xml I have (id_picture_movie, id_movie_realise_date and title).
Also, I don't know how to get properties in List(Movie) inside of MovieResponse.
You aren't actually passing back any items to display after you've set up your recyclerview. So what you're doing is setting it up, sending a network request, and then you do nothing with that data.
val mainAdapter = MainAdapter()
...
private fun findMovies() {
GlobalScope.launch(Dispatchers.Main) {
val results = Networking.showSearchService.getPopularMovies("0b0e8d104f0d6130a4fc67848f89e107")
Log.d("results", results.toString())
//Assuming you're returning a list
mainAdapter.refreshData(results)
}}
}
1) If I were you, I would edit your post to exclude your API key. Take your response and paste in on a site like Pastebin. In the future, try to paste the stacktrace of the error as well as it makes the answers' life much easier.
2) In your response data classes you may need to have #SerializedName for your other values. It's been awhile since I have used Gson as my deserializer, so perhaps that is incorrect. This is likely the reason for this "Also, I don't know how to get properties in List(Movie) inside of MovieResponse."
3) Your return type for the API service is wrong. Here is the result of the API call formatted: https://pastebin.com/TQb0YSty. You can see that the return type is not a list, it is a single JSON object. So your return type of Call<List<MovieResponse>> should be Call<MovieResponse>. This may be what is resulting in the ExecutorCallbackCall error.
There may be other issues, but those are the things that stood out to me right away

Deserializing Scala list with Jackson

I have a case class that returns looks something like this
case class Response(
#JsonDeserialize(contentAs = classOf[java.lang.Long])
longList: List[Long] = null)
I have a customer ObjectMapper, that among other things registers DefaultScalaModule. According to https://github.com/FasterXML/jackson-module-scala/wiki/FAQ, adding #JsonDeserialize should solve the issue, but it doesn't
The issue is in my tests, and I get the following error message
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
at scala.runtime.BoxesRunTime.unboxToLong(BoxesRunTime.java:105)
Test class, list.head is what triggers the error
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ControllerTest {
#Autowired
var testRestTemplate: TestRestTemplate = _
#Autowired
var objectMapper: ObjectMapper = _
#Test
def test() : Unit = {
val response = testRestTemplate.exchange("url", HttpMethod.GET, classOf[Response])
val list = response.getBody.longList
val a = list.head
}
}
Debugging tells me that my list is actually of type $colon$colon containing Integers

Converting impure function to a pure function improvements - Scala

object IO {
def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = {
Http(link)
.param("access_token", apiKey)
.asString
.body
}
}
class SongService {
private def retrieveSongId(songName: String): Option[JsValue] = {
val formattedSongName = songName.replace(" ", "%20")
val searchLink = "https://api.genius.com/search?q=" + formattedSongName
//impure call
val geniusStringResponse = IO.getHtmlFromWebsiteViaHttp(searchLink, apiKey)
//Extra processing on geniusStringResponse
}
}
My current design is I would have a service class which is responsible for getting some information via an external API. Now I understand that it's impossible to have 100% pure functions.
My question: What is the best way to handle situations where you need to connect to an external API in Scala/FP?. The aim is to have the most adequate 'functional programming style' by minimising impure functions
Currently, I am encapsulating all API calls in IO object. Is this suitable enough? I see examples of monads for situations. Should I incorporate a monad style in this case?
This isn't so much an FP problem, as I don't see any problems with your code in terms of FP, but what you should do, in my opinion is use dependency injection, such that, for testing, you can substitute a test class for IO that has a guaranteed response. Something like this:
abstract class IO {
def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String
}
class IOImpl extends IO {
def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = {
Http(link)
.param("access_token", apiKey)
.asString
.body
}
}
class IOTestImpl extends IO {
def getHtmlFromWebsiteViaHttp(link: String, apiKey: String = ""): String = ??? //some test HTML
}
And then in your service:
class SongService(io: IO) {
private def retrieveSongId(songName: String): Option[JsValue] = {
val formattedSongName = songName.replace(" ", "%20")
val searchLink = "https://api.genius.com/search?q=" + formattedSongName
val geniusStringResponse = io.getHtmlFromWebsiteViaHttp(searchLink, apiKey)
//Extra processing on geniusStringResponse
}
}
Then when you instantiate your SongService, pass it IOTestImpl in testing and IOImpl otherwise. You might find some relevant information on dependency injection and database access objects.

Neo4j OGM example with Scala

I tried the example mentioned in Luanne's article The essence of Spring Data Neo4j 4 in Scala. The code can be found in the neo4j-ogm-scala repository.
package neo4j.ogm.scala.domain
import org.neo4j.ogm.annotation.GraphId;
import scala.beans.BeanProperty
import org.neo4j.ogm.annotation.NodeEntity
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
abstract class Entity {
#GraphId
#BeanProperty
var id: Long = _
override def equals(o: Any): Boolean = o match {
case other: Entity => other.id.equals(this.id)
case _ => false
}
override def hashCode: Int = id.hashCode()
}
#NodeEntity
class Category extends Entity {
var name: String = _
def this(name: String) {
this()
this.name = name
}
}
#NodeEntity
class Ingredient extends Entity {
var name: String = _
#Relationship(`type` = "HAS_CATEGORY", direction = "OUTGOING")
var category: Category = _
#Relationship(`type` = "PAIRS_WITH", direction = "UNDIRECTED")
var pairings: Set[Pairing] = Set()
def addPairing(pairing: Pairing): Unit = {
pairing.first.pairings +(pairing)
pairing.second.pairings +(pairing)
}
def this(name: String, category: Category) {
this()
this.name = name
this.category = category
}
}
#RelationshipEntity(`type` = "PAIRS_WITH")
class Pairing extends Entity {
#StartNode
var first: Ingredient = _
#EndNode
var second: Ingredient = _
def this(first: Ingredient, second: Ingredient) {
this()
this.first = first
this.second = second
}
}
object Neo4jSessionFactory {
val sessionFactory = new SessionFactory("neo4j.ogm.scala.domain")
def getNeo4jSession(): Session = {
System.setProperty("username", "neo4j")
System.setProperty("password", "neo4j")
sessionFactory.openSession("http://localhost:7474")
}
}
object Main extends App {
val spices = new Category("Spices")
val turmeric = new Ingredient("Turmeric", spices)
val cumin = new Ingredient("Cumin", spices)
val pairing = new Pairing(turmeric, cumin)
cumin.addPairing(pairing)
val session = Neo4jSessionFactory.getNeo4jSession()
val tx: Transaction = session.beginTransaction()
try {
session.save(spices)
session.save(turmeric)
session.save(cumin)
session.save(pairing)
tx.commit()
} catch {
case e: Exception => // tx.rollback()
} finally {
// tx.commit()
}
}
The problem is that nothing gets saved to Neo4j. Can you please point out the problem in my code?
Thanks,
Manoj.
Scala’s Long is an instance of a Value class. Value classes were explicitly introduced to avoid allocating runtime objects. At the JVM level therefore Scala's Long is equivalent to Java’s primitive long which is why it has the primitive type signature J. It cannot be therefore be null, and should not be used as a graphId. Although Scala mostly will do auto-boxing between its own Long and Java’s Long class, this doesn’t apply to declarations, only to operations on those objects.
The #GraphId isn't being picked up on your entities. I have zero knowledge of Scala but it looks like the scala long isn't liked much by the OGM; var id: java.lang.Long = _ works fine.

Custom json serialization of structured scala case classes

I have some working jackson scala module code for roundtripping scala case classes. Jackson worked great for flat case classes but when I made one which contains a list of other case classes the amount of code I seemed to need was a lot. Consider:
abstract class Message
case class CardDrawn(player: Long, card: Int, mType: String = "CardDrawn") extends Message
case class CardSet(cards: List[CardDrawn], mType: String = "CardSet") extends Message
To get the CardSet to roundtrip to/from json with jackson scala module I used a custom serializer/deserializer written in java:
object ScrumGameMashaller {
val mapper = new ObjectMapper()
val module = new SimpleModule("CustomSerializer")
module.addSerializer(classOf[CardSet], new CardSetSerializer)
module.addDeserializer(classOf[CardSet], new CardSetDeserializer)
val scalaModule = DefaultScalaModule
mapper.registerModule(scalaModule)
mapper.registerModule(module)
def jsonFrom(value: Any): String = {
import java.io.StringWriter
val writer = new StringWriter()
mapper.writeValue(writer, value)
writer.toString
}
private[this] def objectFrom[T: Manifest](value: String): T =
mapper.readValue(value, typeReference[T])
private[this] def typeReference[T: Manifest] = new TypeReference[T] {
override def getType = typeFromManifest(manifest[T])
}
private[this] def typeFromManifest(m: Manifest[_]): Type = {
if (m.typeArguments.isEmpty) { m.runtimeClass }
else new ParameterizedType {
def getRawType = m.runtimeClass
def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray
def getOwnerType = null
}
}
with serializer:
public class CardSetSerializer extends JsonSerializer<CardSet> {
#Override
public void serialize(CardSet cardSet, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeArrayFieldStart("cards");
List<CardDrawn> cardsDrawn = cardSet.cards();
scala.collection.Iterator<CardDrawn> iter = cardsDrawn.iterator();
while(iter.hasNext()){
CardDrawn cd = iter.next();
cdSerialize(jgen,cd);
}
jgen.writeEndArray();
jgen.writeStringField("mType", "CardSet");
jgen.writeEndObject();
}
private void cdSerialize(JsonGenerator jgen, CardDrawn cd) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("player", cd.player());
jgen.writeNumberField("card", cd.card());
jgen.writeEndObject();
}
}
and matching deserializer:
public class CardSetDeserializer extends JsonDeserializer<CardSet> {
private static class CardDrawnTuple {
Long player;
Integer card;
}
#Override
public CardSet deserialize(JsonParser jsonParser, DeserializationContext cxt) throws IOException, JsonProcessingException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode root = oc.readTree(jsonParser);
JsonNode cards = root.get("cards");
Iterator<JsonNode> i = cards.elements();
List<CardDrawn> cardObjects = new ArrayList<>();
while( i.hasNext() ){
CardDrawnTuple t = new CardDrawnTuple();
ObjectNode c = (ObjectNode) i.next();
Iterator<Entry<String, JsonNode>> fields = c.fields();
while( fields.hasNext() ){
Entry<String,JsonNode> f = fields.next();
if( f.getKey().equals("player")) {
t.player = f.getValue().asLong();
} else if( f.getKey().equals("card")){
t.card = f.getValue().asInt();
} else {
System.err.println(CardSetDeserializer.class.getCanonicalName()+ " : unknown field " + f.getKey());
}
}
CardDrawn cd = new CardDrawn(t.player, t.card, "CardDrawn");
cardObjects.add(cd);
}
return new CardSet(JavaConversions.asScalaBuffer(cardObjects).toList(), "CardSet");
}
}
This seems like a lot code to deal with something fairly vanilla in the scala. Can this code be improved (what did I miss that jackson has to make this easy)? Else is there a library which will do structured case classes automatically? The jerkson examples looked easy but that seems to have been abandoned.
Argonaut does a great job. Mark Hibbard helped me out with getting the example below working. All that is needed is to create a codec for the types and it will implicitly add an asJson to your objects to turn them into strings. It will also add a decodeOption[YourClass] to strings to extract an object. The following:
package argonaut.example
import argonaut._, Argonaut._
abstract class Message
case class CardDrawn(player: Long, card: Int, mType: String = "CardDrawn") extends Message
case class CardSet(cards: List[CardDrawn], mType: String = "CardSet") extends Message
object CardSetExample {
implicit lazy val CodecCardSet: CodecJson[CardSet] = casecodec2(CardSet.apply, CardSet.unapply)("cards","mType")
implicit lazy val CodecCardDrawn: CodecJson[CardDrawn] = casecodec3(CardDrawn.apply, CardDrawn.unapply)("player", "card", "mType")
def main(args: Array[String]): Unit = {
val value = CardSet(List(CardDrawn(1L,2),CardDrawn(3L,4)))
println(s"Got some good json ${value.asJson}")
val jstring =
"""{
| "cards":[
| {"player":"1","card":2,"mType":"CardDrawn"},
| {"player":"3","card":4,"mType":"CardDrawn"}
| ],
| "mType":"CardSet"
| }""".stripMargin
val parsed: Option[CardSet] =
jstring.decodeOption[CardSet]
println(s"Got a good object ${parsed.get}")
}
}
outputs:
Got some good json {"cards":[{"player":"1","card":2,"mType":"CardDrawn"},{"player":"3","card":4,"mType":"CardDrawn"}],"mType":"CardSet"}
Got a good object CardSet(List(CardDrawn(1,2,CardDrawn), CardDrawn(3,4,CardDrawn)),CardSet)
The question is old but maybe someone could still find it helpful. Apart from Argonaut, Scala has several Json libraries. Here you can find a list of them updated to the beginning of 2016 (and it still gives you a good overall picture).
Most of them (probably all) should allow you to come up with a drier version of your custom serializer/deserailizer. My preference goes to json4s which aims to provide a single AST across multiple libraries including Jackson (a bit like slf4j does for logging libraries). In this post you can find a working example of a Json custom serializer/deserializer using Json4s and Akka Http.