如何在Scala中将MongoDB返回的JSON格式Greeting数据转为XML
Let's break down what's going wrong in your code and walk through the fixes, tailored specifically for Scala and Akka HTTP (since your route structure looks like Akka HTTP):
First, the core issues in your current code:
- You're passing a
Future[Seq[Greeting]]directly to Jackson'sreadValue()— Jackson has no idea how to handle ScalaFuture(it's an asynchronous wrapper, not the actual data). You need to wait for the future to resolve first. - Jackson's default setup doesn't support Scala case classes — the standard Jackson databind library is built for Java, so you need the Scala module to handle case classes properly.
- You don't need to convert through JSON first — your goal is XML output, so we can serialize the
Seq[Greeting]directly to XML, skipping the unnecessary JSON step.
Solution 1: Manual XML Construction (Simplest, No Extra Dependencies)
Since your Greeting case class is small, building the XML manually is straightforward and avoids adding more libraries. Akka HTTP has built-in support for Scala's native XML type, so this works out of the box.
Here's the corrected route:
import akka.http.scaladsl.server.Directives._ import scala.concurrent.Future import scala.xml.Elem // Your existing case class case class Greeting(msg: String, name: String) path("getXML") { val greetingSeqFuture: Future[Seq[Greeting]] = MongoDAL.fetchAllGreetings() // Handle the asynchronous Future with Akka HTTP's onComplete onComplete(greetingSeqFuture) { case scala.util.Success(greetings) => // Build XML directly from the Seq[Greeting] val xmlResponse: Elem = <greetings> {greetings.map(greeting => <greeting> <msg>{greeting.msg}</msg> <name>{greeting.name}</name> </greeting> )} </greetings> complete(xmlResponse) // Akka HTTP auto-sets Content-Type to application/xml case scala.util.Failure(error) => complete(s"Failed to load greetings: ${error.getMessage}") } }
Solution 2: Using Jackson for XML Serialization (For Complex Classes)
If you need to handle more complex case classes later, using Jackson's XML module with Scala support is a scalable option. First, update your built.sbt with compatible dependencies (your 2.4.0 version is very outdated — use a recent stable version):
libraryDependencies ++= Seq( "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.15.2", "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.15.2", // Keep your existing Akka HTTP and MongoDB dependencies )
Then, adjust your route to use Jackson's XmlMapper (configured for Scala):
import akka.http.scaladsl.server.Directives._ import scala.concurrent.Future import com.fasterxml.jackson.dataformat.xml.XmlMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule // Initialize the mapper once (don't create it inside the route!) val xmlMapper: XmlMapper = XmlMapper.newBuilder() .addModule(DefaultScalaModule) // Enables Scala case class support .build() path("getXML") { val greetingSeqFuture: Future[Seq[Greeting]] = MongoDAL.fetchAllGreetings() onComplete(greetingSeqFuture) { case scala.util.Success(greetings) => // Serialize the Seq[Greeting] directly to XML string val xmlString = xmlMapper.writeValueAsString(greetings) complete(xmlString) case scala.util.Failure(error) => complete(s"Failed to load greetings: ${error.getMessage}") } }
Key Takeaways
- Always resolve Futures in Akka HTTP routes — use
onComplete,onSuccess, oronFailureto handle asynchronous results; never pass a raw Future to serialization methods. - Scala case classes need special Jackson support — the
jackson-module-scalaadds handling for case classes, immutable fields, and other Scala-specific features. - Prefer manual XML for simple types — it's faster, easier to debug, and doesn't require extra dependencies.
内容的提问来源于stack exchange,提问作者Ruchir Dixit

