Skip to content

Instantly share code, notes, and snippets.

@oxbowlakes
Last active December 16, 2017 07:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oxbowlakes/117f0dbfe8eb05347fd25db9ca3c4cd3 to your computer and use it in GitHub Desktop.
Save oxbowlakes/117f0dbfe8eb05347fd25db9ca3c4cd3 to your computer and use it in GitHub Desktop.

In this page of the Spray migration guide, we are told that marshalling a response looks like this:


Marshaller.of can be replaced with Marshaller.withFixedContentType.

Was:

Marshaller.of[JsonApiObject](`application/json`) { (value, contentType, ctx) =>
  ctx.marshalTo(HttpEntity(contentType, value.toJson.toString))
}

Replace with:

Marshaller.withFixedContentType(`application/json`) { obj =>
  HttpEntity(`application/json`, obj.toJson.compactPrint)
}

Let's look at what we are now supposed to write in more detail. Marshaller.withFixedContentType takes two type parameters, A and B. The second argument list is a function A => B. Now it's pretty obvious that the above snippet from the official documentation cannot conceivably compile - how can the compiler infer a type for obj such that the method toJson can be called on it? Evidently I need to supply type parameters! So: what do I supply?

Evidently I know what I am trying to marshal back to the client, let's call this a MyA. What should B be? Well, I should probably look to the documentation's page on marshalling, shouldn't I? Hmmm, searching this page for withFixedContentType merely shows me how the method has been implemented. Say what? Why do I care a fig how the method was implemented? I have a MyA and I'm trying to send it back to the client! Why is the documentation listing a bunch of methods and their implementations instead of showing me how to actually use them?

So; no help here. Well, the first example returns an HttpEntity, so I guess B must be HttpEntity! Let's write my code!

Marshaller.withFixedContentType[MyA, HttpEntity](`application/json`) { myA =>
  HttpEntity(`application/json`, myA.toJsonString /* whatevs */ )
}

Except nothing compiles now. The compiler complains on a line of my application:

complete(StatusCodes.OK -> myA)

I get this error:

Error:(154, 18) type mismatch;
found   : (akka.http.scaladsl.model.StatusCodes.Success, oxbow.MyA)
required: akka.http.scaladsl.marshalling.ToResponseMarshallable
        complete((StatusCodes.OK, myA))    

OK, Now I have no clue whatsoever. This API is based on implicit conversions, evidently. These implicit conversions can obviously only take effect if I have exactly the correct imports and if I have exactly the correct types. Except the migration guide literally lists neither imports nor types for this fundamental use-case. I clearly have to figure this out from first principles.

Back to the guide to akka marshalling - I peruse the section on Predefined Marshallers, and alight on the following:

T, if a ToEntityMarshaller[T] is available

OK! This is it! I need a ToEntityMarshaller! Let's find mentions of ToEntityMarshaller on this page and all will become clear! Oh, hang on, a ToEntityMarshaller[T] is just a type alias for Marshaller[T, MessageEntity]: didn't I already provide one of those? Oh, no, I didn't - I provided a Marshaller[MyA, HttpEntity]. Hmmm, how does an HttpEntity correspond to a MessageEntity?

A few more mouse-clicks around the source code reveals that MessageEntity is a type alias for RequestEntity which is (in turn) a sub-class of HttpEntity. So how do I return a RequestEntity and why does the documentation not seem to return one of those but instead an HttpEntity? I guess that - just possibly - there may be some other implicit conversion in scope. With a promise to the documentation gods that I will sacrifice a lesser mammal, I change my marshaller definition to:

Marshaller.withFixedContentType[MyA, MessageEntity](`application/json`) { myA =>
  HttpEntity(`application/json`, myA.toJsonString /* whatevs */ )
}

IT COMPILES!!! IT ACTUALLY COMPILES

I break down in tears of joy at this point. Until, that is, I remember my promise of sacrifice.

Conclusions

The lack of the correct type annotations and imports in the Spray Documentation guide has just lost me half a day. "Hang, on, how did this take half a day?" I hear you ask; "I just read this whole thing in under five minutes!" Well, this is merely one amongst many issues I hit - causing a biblical quantity of compilation errors. Wading through them all was complicated - there were many wrong turns I made before alighting on the correct solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment