toJSON without outer square brackets
Asked Answered
S

2

6

I am converting a nested list of a specific structure (required by an API) to JSON using toJSON() in jsonlite. However, I need the final JSON to not contain the outer square brackets (also required by an API).

test_list <- list(
    list(formName = "test_title", user_id = "test_userid", rows = list(list(row = 0))), 
    list(formName = "test_title2", user_id = "test_userid2", rows = list(list(row = 0)))
)

jsonlite::toJSON(test_list, pretty = TRUE, auto_unbox = TRUE)

Which gives:

[
  {
    "formName": "test_title",
    "user_id": "test_userid",
    "rows": [
      {
        "row": 0
      }
    ]
  },
  {
    "formName": "test_title2",
    "user_id": "test_userid2",
    "rows": [
      {
        "row": 0
      }
    ]
  }
] 

But I need to remove the first and last square bracket.

I can use purrr::flatten() to remove the top level of the list and thus the square brackets in the JSON, but then toJSON() doesn't seem to like that the list has duplicate names, and renames them to name.1, name.2, name.3 etc (which also isn't allowed by the API).

That is:

jsonlite::toJSON(test_list %>% purrr::flatten(), pretty = TRUE, auto_unbox = TRUE)

Which removes the outer square brackets, but converts the names in the second element to formName.1, user_id.1, rows.1, like so:

{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName.1": "test_title2",
  "user_id.1": "test_userid2",
  "rows.1": [
    {
      "row": 0
    }
  ]
}

But this is what I need:

{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName": "test_title2",
  "user_id": "test_userid2",
  "rows": [
    {
      "row": 0
    }
  ]
}

That is, formName, user_ud and rows in the second JSON element are not appended with ".1"

Any advice would be greatly appreciated!

Sakhuja answered 9/5, 2019 at 6:5 Comment(7)
the expected and input are the same. Did you meant jsonlite::toJSON(test_list %>% purrr::flatten(), pretty = TRUE, auto_unbox = TRUE)Unipersonal
@Unipersonal Thanks for replying, but they are not the same. Notice how the second element in the first example have formName.1, user_id.1 and rows.1 -- these need to be just formName, user_id and rows. However, when using flatten() on the list, which I need to do to remove the square brackets that encapsulate the entire JSON, the ".1" is appended, which the API will not recognise. I have added auto_unbox = TRUE in the example to remove ambiguity.Sakhuja
If you paste your desired result into a JSON validator like this one you'll see that it's not valid--duplicate key names are forbidden in correct JSON. I don't think you'll be able to get toJSON to produce invalid syntax, so I think your best bet is editing the result.Paganize
Save output = toJSON(test_list %>% purrr::flatten(), pretty = TRUE, auto_unbox = TRUE) as an intermediate result, and then use gsub or similar to make the edits you need.Paganize
@Gregor You're absolutely right. I had actually tried that approach but was using str_sub from stringr which converted the json into a character. I had just assumed that gsub would do the same. gsub does the trick, thanks! I'll accept your answer as correctSakhuja
I'm really shocked there's a difference. is.character(toJSON(...)) returns TRUE, but stringr seems to drop the json class...Paganize
It's still unclear whether the expected output is of the form { "formName": "test_title", ... , "formName": "test_title2", ... } (with duplicate keys, corresponding to OP's "But this is what I need"), or { "formName": "test_title", ... }, { "formName": "test_title2", ... } (several valid JSON objects, comma separated, corresponding to OP's "But I need to remove the first and last square bracket"). In case it's the latter, would test_list %>% map_chr(toJSON, pretty = TRUE, auto_unbox = TRUE) %>% paste(collapse = ",\n") %>% cat() do?Spruce
P
3

Just edit the JSON. You can do it with gsub, or with stringr. If you use stringr functions, it will lose it's "json" class, but you can put it back:

> x = jsonlite::toJSON(test_list %>% purrr::flatten(), pretty = TRUE, auto_unbox = TRUE)
> gsub("user_id\\.1", "user_id", x)
{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName.1": "test_title2",
  "user_id": "test_userid2",
  "rows.1": [
    {
      "row": 0
    }
  ]
}

> y = stringr::str_replace_all(x, "user_id\\.1", "user_id")
> class(y) = "json"
> y
{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName.1": "test_title2",
  "user_id": "test_userid2",
  "rows.1": [
    {
      "row": 0
    }
  ]
} 

I'll leave it to you to write appropriate regex to make the substitutions you want.

Paganize answered 9/5, 2019 at 7:2 Comment(0)
U
6

This seems to work (ugly but simple):

df = data.frame(a=1,b=2)
js = toJSON(unbox(fromJSON(toJSON(df))))
> js
{"a":1,"b":2} 

for some reason, auto_unbox=T does not work:

> toJSON(df,auto_unbox = T)
[{"a":1,"b":2}] 
Ulises answered 15/2, 2021 at 13:0 Comment(0)
P
3

Just edit the JSON. You can do it with gsub, or with stringr. If you use stringr functions, it will lose it's "json" class, but you can put it back:

> x = jsonlite::toJSON(test_list %>% purrr::flatten(), pretty = TRUE, auto_unbox = TRUE)
> gsub("user_id\\.1", "user_id", x)
{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName.1": "test_title2",
  "user_id": "test_userid2",
  "rows.1": [
    {
      "row": 0
    }
  ]
}

> y = stringr::str_replace_all(x, "user_id\\.1", "user_id")
> class(y) = "json"
> y
{
  "formName": "test_title",
  "user_id": "test_userid",
  "rows": [
    {
      "row": 0
    }
  ],
  "formName.1": "test_title2",
  "user_id": "test_userid2",
  "rows.1": [
    {
      "row": 0
    }
  ]
} 

I'll leave it to you to write appropriate regex to make the substitutions you want.

Paganize answered 9/5, 2019 at 7:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.