6

I'm trying to create a multi-line String in Scala as below.

val errorReport: String =
    """
      |{
      |"errorName":"blah",
      |"moreError":"blah2",
      |"errorMessage":{
      |         "status": "bad",
      |         "message": "Unrecognized token 'noformatting': was expecting 'null', 'true', 'false' or NaN
 at [Source: (ByteArrayInputStream); line: 1, column: 25]"
      | }
      |}
      """
   .stripMargin

It's a nested JSON and it's not displaying properly when I print it. The message field inside errorMessage (which is the output of calling getMessage on an instance of a Throwable) is causing the issue because it looks like there is a newline right before

at [Source: ....

If I get rid of that line the JSON displays properly. Any ideas on how to properly format this are appreciated.

EDIT: The issue is with the newline character. So I think the question is more concisely - how to handle the newline within the triple quotes so that it's still recognized as a JSON?

EDIT 2: message is being set by a variable like so:

"message": "${ex.getMessage}"

where ex is a Throwable. An example of the contents of that getMessage call is provided above.

covfefe
  • 2,485
  • 8
  • 47
  • 77
  • So, your question is about how to format your Scala code, not about JSON? – Andrey Tyukin Apr 01 '19 at 18:35
  • Yes, it's about what I need to do in Scala to be able to properly read this as a JSON. – covfefe Apr 01 '19 at 18:42
  • Ok, then, is your question duplicate of [this one](https://stackoverflow.com/questions/2392766/multiline-strings-in-json)? – Andrey Tyukin Apr 01 '19 at 19:10
  • Yes except in that one the strings with line breaks are hardcoded so it's possible to put `\n` at the appropriate spots. In mine, I'm passing with `getMessage`. – covfefe Apr 01 '19 at 19:13
  • Does anything prevent you from replacing the line breaks by appropriate escape sequences? (added edit) – Andrey Tyukin Apr 01 '19 at 19:15
  • I don't have the actual string beforehand, so I guess I would have to do something like `ex.getMessage.split("\n")` and then join them back together using the escape sequences. Not sure if that's the best way to do it though. – covfefe Apr 01 '19 at 19:18
  • 1
    have you seen the latest edit? – Andrey Tyukin Apr 01 '19 at 19:19
  • 1
    Just saw it and it worked! Thanks!! Ended up doing this: `"message": "${ex.getMessage.replaceAll("\\n", " ")}"` – covfefe Apr 01 '19 at 19:25

1 Answers1

5

Update 2023: I don't know why anyone would bother generating JSON manually in 2023. Just use some appropriate library that knows how to serialize stuff to JSON.


(original answer)

I assume that your question has nothing to do with JSON, and that you're simply asking how to create very wide strings without violating the horizontal 80-character limit in your Scala code. Fortunately, Scala's string literals have at least the following properties:

  • You can go from ordinary code to string-literal mode using quotes "..." and triple quotes """...""".
  • You can go from string-literal mode to ordinary code mode using ${...}
  • Free monoid over characters is reified as methods, that is, there is the + operation that concatenates string literals.
  • The whole construction can be made robust to whitespace and indentation using | and stripMargin.

All together, it allows you to write down arbitrary string literals without ever violating horizontal character limits, in a way that is robust w.r.t. indentation.

In this particular case, you want to make a line break in the ambient scala code without introducing a line break in your text. For this, you simply

  • exit the string-literal mode by closing """
  • insert concatenation operator + in code mode
  • make a line-break
  • indent however you want
  • re-enter the string-literal mode again by opening """

That is,

"""blah-""" +
"""blah"""

will create the string "blah-blah", without line break in the produced string.


Applied to your concrete problem:

val errorReport: String = (
    """{
      |  "errorName": "blah",
      |  "moreError": "blah2",
      |  "errorMessage": {
      |    "status": "bad",
      |    "message": "Unrecognized token 'noformatting'""" +
    """: was expecting 'null', 'true', 'false' or NaN at """ +
    """[Source: (ByteArrayInputStream); line: 1, column: 25]"
      |  }
      |}
      """
  ).stripMargin

Maybe a more readable option would be to construct the lengthy message separately from the neatly indented JSON, and then use string interpolation to combine the two components:

val errorReport: String = {
  val msg = 
    """Unrecognized token 'noformatting': """ +
    """was expecting 'null', 'true', 'false' or NaN at """ +
    """[Source: (ByteArrayInputStream); line: 1, column: 25]"""

    s"""{
      |  "errorName": "blah",
      |  "moreError": "blah2",
      |  "errorMessage": {
      |    "status": "bad",
      |    "message": "${msg}"
      |  }
      |}
      """
  }.stripMargin

If the message itself contains line breaks

Since JSON does not allow multiline string literals, you have to do something else:

  • To remove line breaks, use .replaceAll("\\n", "") or rather .replaceAll("\\n", " ")
  • To encode line breaks with the escape sequence \n, use .replaceAll("\\n", "\\\\n") (yes... backslashes...)
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • The message field is being set via a variable: `"message": "${ex.getMessage}"` Where `ex` is a `Throwable` so I can't hardcode what the message will be. All I know is that it contains a newline somewhere and that's messing it up. – covfefe Apr 01 '19 at 19:06
  • @covfefe And what do you want to do with the newline? Who is supposed to decide that? JSON does not allow line breaks in the middle of string literals. – Andrey Tyukin Apr 01 '19 at 19:08
  • Yes, JSON doesn't allow line breaks but I want to include it as part of the same field. Maybe by ignoring the line break altogether if that's possible. – covfefe Apr 01 '19 at 19:11