Frage

I am using JSON4S to produce some JSON.

If a condition is met, I would like to produce the following:

{"fld1":"always", "fld2":"sometimes"}

If the condition is not met, I would like to produce:

{"fld1":"always"}

What I have tried so far is:

val fld1 = "fld1" -> "always"

val json = if(condition) ("fld2" -> "sometimes") ~ fld1 else fld1

compact(render(json))

However, this gives me a type mismatch in the render "Found: Product with Serializable. Required: org.json4s.package.JValue".

The interesting this is that render(("fld2" -> "sometimes") ~ fld1) works, and so does render(fld1). The problem seems to be with the type inferred for json.

How could I fix this?

War es hilfreich?

Lösung 2

Not the nicest way I can think of, but declaring the type yourself should work:

val json: JObject = 
  if(condition) ("fld2" -> "sometimes") ~ fld1 else fld1

compact(render(json))

Also, note that you can get type inference to help itself out: If you can render in one go:

compact(render(
  if(condition) fld1 ~ ("fld2" -> "sometimes") else fld1  
))

Andere Tipps

While both of the current answers give suitable workarounds, neither explains what's going on here. The problem is that if we have two types, like this:

trait Foo
trait Bar

And an implicit conversion (or view) from one to the other:

implicit def foo2bar(foo: Foo): Bar = new Bar {}

A conditional with a Foo for its then clause and a Bar for its else clause will still be typed as the least upper bound of Foo and Bar (in this case Object, in your case Product with Serializable).

This is because the type inference system isn't going to step in and say, well, we could view that Foo as a Bar, so I'll just type the whole thing as a Bar.

This makes sense if you think about it—for example, if it were willing to handle conditionals like that, what would it do if we had implicit conversions both ways?

In your case, the then clause is typed as a JObject, and the else clause is a (String, String). We have a view from the latter to the former, but it won't be used here unless you indicate that you want the whole thing to end up as a JObject, either by explicitly declaring the type, or by using the expression in a context where it has to be a JObject.

Given all of that, the simplest workaround may just be to make sure that both clauses are appropriately typed from the beginning, by providing a type annotation like this for fld1:

val fld1: JObject = "fld1" -> "always"

Now your conditional will be appropriately typed as it is, without the type annotation.

Another approach is to box conditional values to Options.

val json = fld1 ~ ("fld2" -> (if (condition) Some("sometimes") else None))

compact(render(json))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top