Parametrize a Pytest using a dictionary with key/value individual pairs mapping - pytest

Have multiple tests in one test class. I would like to use a dictionary to parametrize the class.
Dictionary structure: {key1: [val_1, val2], key2: [val_3, val4]}
Test:
#pytest.mark.parametrize('key, value', [(k, v) for k, l in some_dict.items() for v in l], scope='class')
# ^^^ the best solution that I've found but still not as expected, and without IDS ^^^
class TestSomething:
def test_foo(self):
assert True
def test_boo(self):
assert True
Expected order (ids including, both key and values are objects and can provide '.name' property):
<Class TestSomething>
<Function test_foo[key1_name-val1_name]>
<Function test_boo[key1_name-val1_name]>
<Function test_foo[key1_name-val2_name]>
<Function test_boo[key1_name-val2_name]>
<Function test_foo[key2_name-val3_name]>
<Function test_boo[key2_name-val3_name]>
<Function test_foo[key2_name-val4_name]>
<Function test_boo[key2_name-val4_name]>
How can I add ids for this parametrize?

Here is a solution using a call to an external function in charge of formatting names from parameters value.
def idfn(val):
# receive here each val
# so you can return a custom property
return val.name
#pytest.mark.parametrize(
"key, value",
[(k, v) for k, l in some_dict.items() for v in l],
scope="class",
ids=idfn,
)
class TestSomething:
def test_foo(self, key, value):
assert True
But the simple solution with a lambda suggested by MrBean also works. In your simple case I would pick this one and use the external function only when more complex formatting is required.
#pytest.mark.parametrize(
"key, value",
[(k, v) for k, l in some_dict.items() for v in l],
scope="class",
ids=lambda val: val.name,
)
class TestSomething:
def test_foo(self, key, value):
assert True
The available options are presented in the doc

Related

Scala: how to upsert field value in Monocle

Given the JsonExample in the monocle project, I would like to create a lens where a set call will either replace a value in a key/value pair, or create the key/value pair if it doesnt already exist.
However this seems to represented with either an index (which can compose type safe) or an at, which does not type safe
//for replacing:
(jsObject composeOptional index("age") composePrism jsNumber).set(45)
//for creating:
(jsObject composeLens at("age")).set(JsNumber(45)) <- will accept any old json
Is what I am after possible?
Also could I extend it, such that if age was nested in another JsObject, for example:
val n = (jsObject composeOptional index("nested") composePrism
jsObject composeOptional index("age") composePrism jsNumber).set(45)
Where the key/value pair for "nested" didnt yet exist, that it would create the object at nested and then add the field
n(JsObject(Map.empty)) -> JsObject(Map("nested" -> JsObject("age" -> JsNumber(45)))
let's have a look at index and at signature for JsObject:
def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]
at is a Lens so its target ('Option[Json]') is always present. It means that we can add, delete and update the Json element at any field of a JsonObject.
import argonaut._, Argonaut._
import monocle.function._
(jObjectPrism composeLens at("name")).set(Some(jString("John")))(Json())
> res0: argonaut.Json = {"name":"John"}
(jObjectPrism composeLens at("name")).set(Some(jString("Robert")))(res0)
> res1: argonaut.Json = {"name":"Robert"}
(jObjectPrism composeLens at("name")).set(None)(res0)
> res2: argonaut.Json = {}
On the other hand, index is an Optional so it is target (Json) may or may not be there. It means that index can only update values but cannot add or delete.
(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}
(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}
So to come back to your original question, if you want to add or update value at a particular field, you need to use at and wrap the Json in a Some (see res1), it will overwrite or create the Json at that field.
At the moment there is very specific behaviour in the library.
When you composing Optional with some Iso or Prism it's downgrading right hand side argument to POptional and that's trouble.
Iso[A,B] and Prism[A,B] differs from Lens[A,B] and Optional[A,B] in sense that reverseGet can create element of A totally from B while set needs original value of A
So when for Optional and Lens it's totally legit that you can not modify part of the value, while not having this value in orignal Map or JsObject, for Iso and Prism you can define another behaviour.
And while waiting for the issue to be discussed, you can use following workaround
implicit class POptStrictComposition[S, T, A, B](self: POptional[S, T, A, B]) {
def sComposePrism[C, D](other: PPrism[A, B, C, D]) = new POptional[S, T, C, D] {
def getOrModify(s: S): T \/ C =
self.getOrModify(s).flatMap(a => other.getOrModify(a).bimap(self.set(_)(s), identity))
def set(d: D): S => T =
self.set(other.reverseGet(d))
def getOption(s: S): Option[C] =
self.getOption(s) flatMap other.getOption
def modifyF[F[_] : Applicative](f: C => F[D])(s: S): F[T] =
self.modifyF(other.modifyF(f))(s)
def modify(f: C => D): S => T =
self.modify(other.modify(f))
}
def ^!<-?[C, D](o: PPrism[A, B, C, D]) = sComposePrism(o)
def sComposeIso[C, D](other: PIso[A, B, C, D]) = sComposePrism(other.asPrism)
def ^!<->[C, D](o: PIso[A, B, C, D]) = sComposeIso(o)
}
now you can try to change your code to
(jsObject composeOptional index("age") sComposePrism jsNumber).set(45)
and report if it helped

Restore a dependent type at runtime

I'm trying to restore a dependent type in Scala at runtime. I basically want to archive a type-save map, where every key has an associated type, but all type informations of the stored key value pairs aren't visible to the user of Map (unlike the awesome Shapeless Map).
class Key[V] {
type Value = V
def ->(value: V) = Pair(this, value)
}
trait Pair {
val key: Key[_]
val value: key.Value
}
trait Map {
val pairs: Seq[Pair]
def get[V](key: Key[V]): Option[V] =
pairs.find(pair => pair.key eq key).map(_.value).asInstanceOf[Option[V]]
// ^ ^
// the runtime prove that pair.key.Value equals V |
// |
// 'Convince' the compile that I know what I do
}
Usage:
val text = new Key[String]
val count = new Key[Int]
val map: Map = new Map { val pairs = text -> "Hello World!" :: Nil }
map.get(text) // Some(Hello World!), Type: Option[String]
map.get(count) // None, Type: Option[Int]
Is it possible to write a get method without using a cast explicit with asInstanceOf or implicit with a match with an unchecked branch?
I tried to write an unapply for pairs, but run into the same problem.
Note that I leave out the definition of the Pair-companion object. Here a running example in a Gist.
Remember the JVM erases generics at runtime. So anything that relies on generics, including dependent typing, can only happen at compile time - i.e. in the caller, because any given method will only compile to one runtime code path. The only alternative is checking the runtime class (either directly or by pattern matching) as you say. (Shapeless has a type-safe, typeclass-driven helper if you go down that route)
There might be a clever way to express your requirements without the type issues, but in general the type information has to be either visible to the caller or checked at runtime.
Many way to solve your type issue. First of all define source of issue:
trait Map {
val pairs: Seq[Pair] // (1)
def get[V](key: Key[V]): Option[V] = // (2)
pairs.find(_.key eq key).map{_.value } // (3)
}
pairs type is Seq of Pair (with some embedded undefined type key: Key[_])
key type is Key[V] and expected result type is Option[V]
try to return type from (1) Key[_] instead of expected Key[V] and extracted V
Solution: you should guaranty that pairs embedded type of key is the same what you return
One of possible solutions:
trait Key[V] {
def ->(value: V) = Pair(this, value)
}
trait Pair {
type Value
val key: Key[Value]
val value: Value
}
trait Map1[V] {
val pairs: Seq[Pair {type Value = V } ]
def get(key: Key[V]): Option[V] =
pairs.find(_.key eq key).map{ _.value }
}
trait Map2 {
type Value
val pairs: Seq[Pair {type Value = Map2.this.Value} ]
def get[V >: Map2.this.Value](key: Key[V]): Option[V] =
pairs.find(_.key eq key).map{ _.value }
}

Is it possible to work with a list of generic values with different type parameters in Scala?

I want to achieve the following:
There is a list of strings I need to process.
There are several different kinds of these processors, each of which knows which part of the string to read.
I need to work in 2 phases: first, processors need to see each input string to build processor-specific data; second, each input string is processed by each of the processors, and the resulting strings are combined into one.
It's easy to do it in a mutable way: there's a common base class for all processors, the different kinds of data they aggregate is encapsulated in the concrete implementations; the interface consists of just 2 functions --- "look at input string and build internal data" and "process input string using your internal data."
As I am writing it in Scala, I am wondering if there exists a pure functional approach. The problem is that now the base trait for these processors is parameterized by the type of their internal data, and there doesn't seem to be a way to have a list of processors of different kinds.
This problem can be demonstrated on a simpler case: say I'd stick with the mutable approach, but for some reason have parameterized the type of what the processor takes from the string:
trait F[V] {
def get(line: String) : V
def aggregate(value: V)
def process(value: V) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
for (s <- List("string1", "string2");
f <- List(new F1(), new F2())
{
f.aggregate(f.get(s)); // Whoops --- doesn't work
}
It doesn't work because f.get(s) returns Any. Looks like I need to express in Scala's type system that List(new F1(), new F2()) contains F[?] that are different but consistent in that if I take an element of that list, it has some concrete value of its type parameter, and f.get(s) is of that type, which should be accepted by f.aggregate().
In the end, I would like to have something like this (with omissions because I don't get how to do it):
trait F[D] {
def initData : D
def aggregate(line: String, data: D) : D
def process(line: String, data: D) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
// Phase 1
// datas --- List of f.initData, how to?
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// update fdata with f.aggregate(s, fdata)
}
}
// Phase 2
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// for all fs, concatenate f.process(s, fdata) into an output string
}
}
Questions:
Is this task solvable in pure functional way in Scala?
Is this task solvable in other functional languages?
This situation looks like quite a general one. Is there a name for it I could search?
Where is the best place to read about it, assuming little to no background on theory of types and functional programming languages?
Also, you may use abstract types instead of generics, so:
trait F {
type D
def initData: D
def aggregate(line: String, data: D): D
def process(line: String, data: D): String
}
class F1 extends F { type D = Int } // ...
class F2 extends F { type D = Map[Int, Int] } // ...
val strings = List("string1", "string2")
for (f <- List(new F1(), new F2())) {
val d = strings.foldLeft(f.initData) { (d, s) => f.aggregate(s, d) }
for (s <- strings)
f.process(s, d)
}
Don't sure, if I undrestood correct order of operation, but it may be a starting point.
Edit Just noticed, that my former solution was overly verbose, consing up a temporary data structure without any need.
I am not sure, what you mean with "purely functional". The following solution (if it is a solution to your problem) is "purely functional", as it has no side effects except the final println call in main.
Note, that the List[F[_]](...) is important, since otherwise, the compiler will infer a very specific internal type for the elements of the list, which doesn't go well with the aggregateAndProcess function.
trait F[D] {
type Data = D // Abbreviation for easier copy+paste below. Does not
// contribute to the actual solution otherwise
def initData: Data
def aggregate(line: String, data: Data) : Data
def process(line: String, aggData: Data): String
}
class F1 extends F[Int] {
def initData: Data = 1
def aggregate(line: String, data: Data) : Data = data + 1
def process(line: String, aggData: Data): String = line + "/F1" + aggData
}
class F2 extends F[Boolean] {
def initData: Data = false
def aggregate(line: String, data: Data) : Data = !data
def process(line: String, aggData: Data): String = line + "/F2" + aggData
}
object Main {
private def aggregateAndProcess[T](line: String, processor: F[T]): String =
processor.process(line, processor.aggregate(line, processor.initData))
def main(args: Array[String]) {
val r = for {
s <- List("a", "b")
d <- List[F[_]](new F1, new F2)
} yield
aggregateAndProcess(s, d)
println(r.toList)
}
}
Note, though, that I am still unsure as to what you actually want to accomplish. The F interface doesn't really specify, which information flows from which method into whatever location at what time, so: this is still a best-guess efford.

Method parameters validation in Scala, with for comprehension and monads

I'm trying to validate the parameters of a method for nullity but i don't find the solution...
Can someone tell me how to do?
I'm trying something like this:
def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[Error,Category] = {
val errors: Option[String] = for {
_ <- Option(user).toRight("User is mandatory for a normal category").right
_ <- Option(parent).toRight("Parent category is mandatory for a normal category").right
_ <- Option(name).toRight("Name is mandatory for a normal category").right
errors : Option[String] <- Option(description).toRight("Description is mandatory for a normal category").left.toOption
} yield errors
errors match {
case Some(errorString) => Left( Error(Error.FORBIDDEN,errorString) )
case None => Right( buildTrashCategory(user) )
}
}
If you're willing to use Scalaz, it has a handful of tools that make this kind of task more convenient, including a new Validation class and some useful right-biased type class instances for plain old scala.Either. I'll give an example of each here.
Accumulating errors with Validation
First for our Scalaz imports (note that we have to hide scalaz.Category to avoid the name conflict):
import scalaz.{ Category => _, _ }
import syntax.apply._, syntax.std.option._, syntax.validation._
I'm using Scalaz 7 for this example. You'd need to make some minor changes to use 6.
I'll assume we have this simplified model:
case class User(name: String)
case class Category(user: User, parent: Category, name: String, desc: String)
Next I'll define the following validation method, which you can easily adapt if you move to an approach that doesn't involve checking for null values:
def nonNull[A](a: A, msg: String): ValidationNel[String, A] =
Option(a).toSuccess(msg).toValidationNel
The Nel part stands for "non-empty list", and a ValidationNel[String, A] is essentially the same as an Either[List[String], A].
Now we use this method to check our arguments:
def buildCategory(user: User, parent: Category, name: String, desc: String) = (
nonNull(user, "User is mandatory for a normal category") |#|
nonNull(parent, "Parent category is mandatory for a normal category") |#|
nonNull(name, "Name is mandatory for a normal category") |#|
nonNull(desc, "Description is mandatory for a normal category")
)(Category.apply)
Note that Validation[Whatever, _] isn't a monad (for reasons discussed here, for example), but ValidationNel[String, _] is an applicative functor, and we're using that fact here when we "lift" Category.apply into it. See the appendix below for more information on applicative functors.
Now if we write something like this:
val result: ValidationNel[String, Category] =
buildCategory(User("mary"), null, null, "Some category.")
We'll get a failure with the accumulated errors:
Failure(
NonEmptyList(
Parent category is mandatory for a normal category,
Name is mandatory for a normal category
)
)
If all of the arguments had checked out, we'd have a Success with a Category value instead.
Failing fast with Either
One of the handy things about using applicative functors for validation is the ease with which you can swap out your approach to handling errors. If you want to fail on the first instead of accumulating them, you can essentially just change your nonNull method.
We do need a slightly different set of imports:
import scalaz.{ Category => _, _ }
import syntax.apply._, std.either._
But there's no need to change the case classes above.
Here's our new validation method:
def nonNull[A](a: A, msg: String): Either[String, A] = Option(a).toRight(msg)
Almost identical to the one above, except that we're using Either instead of ValidationNEL, and the default applicative functor instance that Scalaz provides for Either doesn't accumulate errors.
That's all we need to do to get the desired fail-fast behavior—no changes are necessary to our buildCategory method. Now if we write this:
val result: Either[String, Category] =
buildCategory(User("mary"), null, null, "Some category.")
The result will contain only the first error:
Left(Parent category is mandatory for a normal category)
Exactly as we wanted.
Appendix: Quick introduction to applicative functors
Suppose we have a method with a single argument:
def incremented(i: Int): Int = i + 1
And suppose also that we want to apply this method to some x: Option[Int] and get an Option[Int] back. The fact that Option is a functor and therefore provides a map method makes this easy:
val xi = x map incremented
We've "lifted" incremented into the Option functor; that is, we've essentially changed a function mapping Int to Int into one mapping Option[Int] to Option[Int] (although the syntax muddies that up a bit—the "lifting" metaphor is much clearer in a language like Haskell).
Now suppose we want to apply the following add method to x and y in a similar fashion.
def add(i: Int, j: Int): Int = i + j
val x: Option[Int] = users.find(_.name == "John").map(_.age)
val y: Option[Int] = users.find(_.name == "Mary").map(_.age) // Or whatever.
The fact that Option is a functor isn't enough. The fact that it's a monad, however, is, and we can use flatMap to get what we want:
val xy: Option[Int] = x.flatMap(xv => y.map(add(xv, _)))
Or, equivalently:
val xy: Option[Int] = for { xv <- x; yv <- y } yield add(xv, yv)
In a sense, though, the monadness of Option is overkill for this operation. There's a simpler abstraction—called an applicative functor—that's in-between a functor and a monad and that provides all the machinery we need.
Note that it's in-between in a formal sense: every monad is an applicative functor, every applicative functor is a functor, but not every applicative functor is a monad, etc.
Scalaz gives us an applicative functor instance for Option, so we can write the following:
import scalaz._, std.option._, syntax.apply._
val xy = (x |#| y)(add)
The syntax is a little odd, but the concept isn't any more complicated than the functor or monad examples above—we're just lifting add into the applicative functor. If we had a method f with three arguments, we could write the following:
val xyz = (x |#| y |#| z)(f)
And so on.
So why bother with applicative functors at all, when we've got monads? First of all, it's simply not possible to provide monad instances for some of the abstractions we want to work with—Validation is the perfect example.
Second (and relatedly), it's just a solid development practice to use the least powerful abstraction that will get the job done. In principle this may allow optimizations that wouldn't otherwise be possible, but more importantly it makes the code we write more reusable.
I completely support Ben James' suggestion to make a wrapper for the null-producing api. But you'll still have the same problem when writing that wrapper. So here are my suggestions.
Why monads why for comprehension? An overcomplication IMO. Here's how you could do that:
def buildNormalCategory
( user: User, parent: Category, name: String, description: String )
: Either[ Error, Category ]
= Either.cond(
!Seq(user, parent, name, description).contains(null),
buildTrashCategory(user),
Error(Error.FORBIDDEN, "null detected")
)
Or if you insist on having the error message store the name of the parameter, you could do the following, which would require a bit more boilerplate:
def buildNormalCategory
( user: User, parent: Category, name: String, description: String )
: Either[ Error, Category ]
= {
val nullParams
= Seq("user" -> user, "parent" -> parent,
"name" -> name, "description" -> description)
.collect{ case (n, null) => n }
Either.cond(
nullParams.isEmpty,
buildTrashCategory(user),
Error(
Error.FORBIDDEN,
"Null provided for the following parameters: " +
nullParams.mkString(", ")
)
)
}
If you like the applicative functor approach of #Travis Brown's answer, but you don't like the Scalaz syntax or otherwise just don't want to use Scalaz, here is a simple library which enriches the standard library Either class to act as an applicative functor validation: https://github.com/youdevise/eithervalidation
For example:
import com.youdevise.eithervalidation.EitherValidation.Implicits._
def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[List[Error], Category] = {
val validUser = Option(user).toRight(List("User is mandatory for a normal category"))
val validParent = Option(parent).toRight(List("Parent category is mandatory for a normal category"))
val validName = Option(name).toRight(List("Name is mandatory for a normal category"))
Right(Category)(validUser, validParent, validName).
left.map(_.map(errorString => Error(Error.FORBIDDEN, errorString)))
}
In other words, this function will return a Right containing your Category if all of the Eithers were Rights, or it will return a Left containing a List of all the Errors, if one or more were Lefts.
Notice the arguably more Scala-ish and less Haskell-ish syntax, and a smaller library ;)
Lets suppose you have completed Either with the following quick and dirty stuff:
object Validation {
var errors = List[String]()
implicit class Either2[X] (x: Either[String,X]){
def fmap[Y](f: X => Y) = {
errors = List[String]()
//println(s"errors are $errors")
x match {
case Left(s) => {errors = s :: errors ; Left(errors)}
case Right(x) => Right(f(x))
}
}
def fapply[Y](f: Either[List[String],X=>Y]) = {
x match {
case Left(s) => {errors = s :: errors ; Left(errors)}
case Right(v) => {
if (f.isLeft) Left(errors) else Right(f.right.get(v))
}
}
}
}}
consider a validation function returning an Either:
def whenNone (value: Option[String],msg:String): Either[String,String] =
if (value isEmpty) Left(msg) else Right(value.get)
an a curryfied constructor returning a tuple:
val me = ((user:String,parent:String,name:String)=> (user,parent,name)) curried
You can validate it with :
whenNone(None,"bad user")
.fapply(
whenNone(Some("parent"), "bad parent")
.fapply(
whenNone(None,"bad name")
.fmap(me )
))
Not a big deal.

JSON to XML in Scala and dealing with Option() result

Consider the following from the Scala interpreter:
scala> JSON.parseFull("""{"name":"jack","greeting":"hello world"}""")
res6: Option[Any] = Some(Map(name -> jack, greeting -> hello world))
Why is the Map returned in Some() thing? And how do I work with it?
I want to put the values in an xml template:
<test>
<name>name goes here</name>
<greeting>greeting goes here</greeting>
</test>
What is the Scala way of getting my map out of Some(thing) and getting those values in the xml?
You should probably use something like this:
res6 collect { case x: Map[String, String] => renderXml(x) }
Where:
def renderXml(m: Map[String, String]) =
<test><name>{m.get("name") getOrElse ""}</name></test>
The collect method on Option[A] takes a PartialFunction[A, B] and is a combination of filter (by a predicate) and map (by a function). That is:
opt collect pf
opt filter (a => pf isDefinedAt a) map (a => pf(a))
Are both equivalent. When you have an optional value, you should use map, flatMap, filter, collect etc to transform the option in your program, avoiding extracting the option's contents either via a pattern-match or via the get method. You should never, ever use Option.get - it is the canonical sign that you are doing it wrong. Pattern-matching should be avoided because it represents a fork in your program and hence adds to cyclomatic complexity - the only time you might wish to do this might be for performance
Actually you have the issue that the result of the parseJSON method is an Option[Any] (the reason is that it is an Option, presumably, is that the parsing may not succeed and Option is a more graceful way of handling null than, well, null).
But the issue with my code above is that the case x: Map[String, String] cannot be checked at runtime due to type erasure (i.e. scala can check that the option contains a Map but not that the Map's type parameters are both String. The code will get you an unchecked warning.
An Option is returned because parseFull has different possible return values depending on the input, or it may fail to parse the input at all (giving None). So, aside from an optional Map which associates keys with values, an optional List can be returned as well if the JSON string denoted an array.
Example:
scala> import scala.util.parsing.json.JSON._
import scala.util.parsing.json.JSON._
scala> parseFull("""{"name":"jack"}""")
res4: Option[Any] = Some(Map(name -> jack))
scala> parseFull("""[ 100, 200, 300 ]""")
res6: Option[Any] = Some(List(100.0, 200.0, 300.0))
You might need pattern matching in order to achieve what you want, like so:
scala> parseFull("""{"name":"jack","greeting":"hello world"}""") match {
| case Some(m) => Console println ("Got a map: " + m)
| case _ =>
| }
Got a map: Map(name -> jack, greeting -> hello world)
Now, if you want to generate XML output, you can use the above to iterate over the key/value pairs:
import scala.xml.XML
parseFull("""{"name":"jack","greeting":"hello world"}""") match {
case Some(m: Map[_,_]) =>
<test>
{
m map { case (k,v) =>
XML.loadString("<%s>%s</%s>".format(k,v,k))
}
}
</test>
case _ =>
}
parseFull returns an Option because the string may not be valid JSON (in which case it will return None instead of Some).
The usual way to get the value out of a Some is to pattern match against it like this:
result match {
case Some(map) =>
doSomethingWith(map)
case None =>
handleTheError()
}
If you're certain the input will always be valid and so you don't need to handle the case of invalid input, you can use the get method on the Option, which will throw an exception when called on None.
You have two separate problems.
It's typed as Any.
Your data is inside an Option and a Map.
Let's suppose we have the data:
val x: Option[Any] = Some(Map("name" -> "jack", "greeting" -> "hi"))
and suppose that we want to return the appropriate XML if there is something to return, but not otherwise. Then we can use collect to gather those parts that we know how to deal with:
val y = x collect {
case m: Map[_,_] => m collect {
case (key: String, value: String) => key -> value
}
}
(note how we've taken each entry in the map apart to make sure it maps a string to a string--we wouldn't know how to proceed otherwise. We get:
y: Option[scala.collection.immutable.Map[String,String]] =
Some(Map(name -> jack, greeting -> hi))
Okay, that's better! Now if you know which fields you want in your XML, you can ask for them:
val z = for (m <- y; name <- m.get("name"); greet <- m.get("greeting")) yield {
<test><name>{name}</name><greeting>{greet}</greeting></test>
}
which in this (successful) case produces
z: Option[scala.xml.Elem] =
Some(<test><name>jack</name><greeting>hi</greeting></test>)
and in an unsuccessful case would produce None.
If you instead want to wrap whatever you happen to find in your map in the form <key>value</key>, it's a bit more work because Scala doesn't have a good abstraction for tags:
val z = for (m <- y) yield <test>{ m.map { case (tag, text) => xml.Elem(null, tag, xml.Null, xml.TopScope, xml.Text(text)) }}</test>
which again produces
z: Option[scala.xml.Elem] =
Some(<test><name>jack</name><greeting>hi</greeting></test>)
(You can use get to get the contents of an Option, but it will throw an exception if the Option is empty (i.e. None).)