0

Seems like a really simple question and it's not clear what the correct answer is here.

We understand that backslashes are a special delimiter in JSON.

From our database we're being returned a field with a backslash in it. It has to be a single backslash for contractual/legal/government representation reasons. Yet it seems to be impossible to return just one single backslash. Wondering if this a rule from JSON? It might be, but 3 of us for spending a day searching can't find out what's going on here.

Here is the FastAPI app:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {
        "backslash_1": f" \ ",
        "backslash_2": f" \\ ",
        "backslash_3": f" \\\ ",
        "backslash_4": f" \\\\ ",
        "backslash_5": f" \\\\\ ",
        "backslash_6": f" \\\\\\ ",
    }

Here's the JSON response:

{
    "backslash_1":" \\ ",  <-- there are 2
    "backslash_2":" \\ ",
    "backslash_3":" \\\\ ",  <-- there are 4
    "backslash_4":" \\\\ ",
    "backslash_5":" \\\\\\ ",  <-- there are 6
    "backslash_6":" \\\\\\ "
}

We're not talking about python r'' or repr() or print, we talking about JSON body response from an API. This question strictly relates to API JSON bodies so these other SO qs aren't useful here:

We've tried all these but this is not really helpful for our API clients users, as they're seeing JSON property value string returned as "ref\\\\official", which is an erroneous response, because the official string should be "ref\official".

The actual advice on whether it's possible or not to return a single slash would be really helpful.

Chris
  • 18,724
  • 6
  • 46
  • 80
Williams
  • 4,044
  • 1
  • 37
  • 53
  • Your Python code is not creating strings with 1-6 backslashes, as their names would indicate: the actual numbers are 1, 1, 2, 2, 3, 3. If you actually *wanted* 1-6, you should have prefixed the strings with `r`, rather than the pointless `f`. – jasonharper May 15 '22 at 03:12
  • `"\"` is not valid JSON, period. If some legal document stipulates that you must return this, the document is being stupid because impossible. If you’re supposed to return a JSON string *expressing* one backslash, `"\\"` is what you want. – deceze May 15 '22 at 07:26

2 Answers2

0

As per FastAPI documentation:

By default, FastAPI would automatically convert that return value to JSON using the jsonable_encoder explained in JSON Compatible Encoder.

Then, behind the scenes, it would put that JSON-compatible data (e.g. a dict) inside of a JSONResponse that would be used to send the response to the client.

Thus, to avoid the conversion (serialisation) to JSON - which causes the doubling of backslashes - you could instead return a custom Response. As per the documentation (link referred above):

When you return a Response directly, its data is not validated, converted (serialized), nor documented automatically.

Response accepts a bytes object or a string (which FastAPI encodes to bytes behind the scenes) in the content parameter, as described in the documentation here and here (from Starlette's documentation). Example below:

from fastapi import Response

@app.get("/")
def root():
    db_resp = '{"message": " \ "}'
    return Response(content=db_resp, status_code=200)

Note: Since " \ " is not a valid JSON string, you shouldn't attempt to deserialise it on client side, as it wouldn't work (no matter whether or not you included media_type="application/json" in the Response). For example, in Python requests, you should use the .text attribute, instead of .json() method, to receive the response:

import requests 

r = requests.get(url="http://localhost:8000/")
print(r.text)
#print(r.json())  # this would not work
Chris
  • 18,724
  • 6
  • 46
  • 80
-1

OK, now having asked have found the answer. It is this one:

https://stackoverflow.com/a/49763737/163567

Yes, it is impossible -- by design.

A JSON parser is, by nature, supposed to emit only valid JSON. From RFC 8259, emphasis mine:

  1. Strings

The representation of strings is similar to conventions used in the C family of programming languages. A string begins and ends with quotation marks. All Unicode characters may be placed within the quotation marks, except for the characters that MUST be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).

Any character may be escaped. If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), then it may be represented as a six-character sequence: a reverse solidus, followed by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. The hexadecimal letters A through F can be uppercase or lowercase. So, for example, a string containing only a single reverse solidus character may be represented as "\u005C".

Alternatively, there are two-character sequence escape representations of some popular characters. So, for example, a string containing only a single reverse solidus character may be represented more compactly as "\".

Note the phrase "MUST be escaped" -- "MUST" is a formally-defined term-of-art; something which does not comply with a MUST requirement from the JSON specification is not allowed to call itself JSON.

In summary: A string containing only a literal backslash in your data may be encoded in JSON as "\u005c", or "\". It may not be encoded as "" (including that character as an unescaped literal).

Williams
  • 4,044
  • 1
  • 37
  • 53