Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala create multi-line JSON String

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.

like image 631
covfefe Avatar asked Apr 01 '19 18:04

covfefe


People also ask

How do I create a multiline in JSON?

JSON does not allow "real" newlines in its data; it can only have escaped newlines. See the answer from @YOU. According to the question, it looks like you attempted to escape line breaks in Python two ways: by using the line continuation character ( "\" ) or by using "\n" as an escape.

Are multi line strings allowed in JSON?

Now you can store multiline strings and get results in JSON single line. It's not the prettiest thing, but it is the simplest given the constraints of JSON.

Can JSON have line breaks?

JSON strings do not allow real newlines in its data; it can only have escaped newlines.

What is multiline in JSON?

Spark JSON data source API provides the multiline option to read records from multiple lines. By default, spark considers every record in a JSON file as a fully qualified record in a single line hence, we need to use the multiline option to process JSON from multiple lines.


1 Answers

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...)
like image 186
Andrey Tyukin Avatar answered Oct 22 '22 14:10

Andrey Tyukin