将R数据帧转换为不同深度的json

问题描述 投票:0回答:1

我将一些嵌套的 json 文件(由 i18n 翻译系统使用)转换为 csv,以允许非 IT 同事查看语言,而无需直接处理 json 文件。

因此,我必须将这些 csv 转换回 json。我试图这样做时失去了理智,使用很多条件来检查每行的嵌套深度以及哪个键与上一行相比发生了变化。然后修改列表或列表中的列表,或列表中列表中的列表......我最终得到了一个混乱的意大利面条代码,但还不能完全工作。我最终会成功完成这项任务(或者变得疯狂)。

但是,我想知道是否有更直接的方法,也许使用另一个库或使用一些我不知道的 R 魔法。

下面是一个带有玩具数据框的表示(真实的数据框有更多行,但深度相同)和预期输出。

library(tibble)
library(jsonlite)

df <- tribble(
  ~ a, ~ b, ~ c, ~ d, ~ value,
  "a", "c", NA, NA,  "q",
  "a", "d", NA, NA, "r",
  "a", "e", NA, NA, "s",
  "a", "f", "i", NA, "t",
  "a", "f", "j", NA, "u",
  "b", "g", NA, NA, "v",
  "b", "h", "k", "m", "x",
  "b", "h", "k", "n", "y",
  "b", "h", "k", "o", "z",
  "b", "h", "k", "p", "aa",
  "b", "h", "l", NA,  "ab"
)

df
#> # A tibble: 11 × 5
#>    a     b     c     d     value
#>    <chr> <chr> <chr> <chr> <chr>
#>  1 a     c     <NA>  <NA>  q    
#>  2 a     d     <NA>  <NA>  r    
#>  3 a     e     <NA>  <NA>  s    
#>  4 a     f     i     <NA>  t    
#>  5 a     f     j     <NA>  u    
#>  6 b     g     <NA>  <NA>  v    
#>  7 b     h     k     m     x    
#>  8 b     h     k     n     y    
#>  9 b     h     k     o     z    
#> 10 b     h     k     p     aa   
#> 11 b     h     l     <NA>  ab

expected <- list(
  a = list(
    c = "q", 
    d = "r",
    e = "s",
    f = list(
      i = "t",
      j = "u"
    )
  ),
  b = list(
    g = "v",
    h = list(
      k = list(
        m = "x",
        n = "y",
        o = "z",
        p = "aa"
      ),
      l = "ab"
    )
    
  )
)

toJSON(expected, auto_unbox = T, pretty = T)
#> {
#>   "a": {
#>     "c": "q",
#>     "d": "r",
#>     "e": "s",
#>     "f": {
#>       "i": "t",
#>       "j": "u"
#>     }
#>   },
#>   "b": {
#>     "g": "v",
#>     "h": {
#>       "k": {
#>         "m": "x",
#>         "n": "y",
#>         "o": "z",
#>         "p": "aa"
#>       },
#>       "l": "ab"
#>     }
#>   }
#> }

创建于 2023-09-14,使用 reprex v2.0.2

r json tidyverse jsonlite
1个回答
0
投票

你可以做一些递归分支。例如

first_split <- function(df, val_col="value") {
  cval <- df[,1]
  nval <- df[,2]
  terminal <- is.na(nval) | names(df[2])==val_col
  out <- list()
  if (any(terminal)) {
    out <- append(out, setNames(as.list(df[[val_col]][terminal]), cval[terminal]))
  }
  if (any(!terminal)) {
    out <- append(out, split(df[!terminal,-1], df[!terminal,1]) |>
      lapply(FUN=first_split))
  }
  out
}

您基本上会查看前两列。第一列是名称,如果下一列是 NA(或者您用完了),则您创建一个节点,否则您将剩余的值拆分到它们自己的列表中。

将其用于我们得到的测试数据

jsonlite::toJSON(first_split(df), auto_unbox=TRUE, pretty=TRUE)
{
  "a": {
    "c": "q",
    "d": "r",
    "e": "s",
    "f": {
      "i": "t",
      "j": "u"
    }
  },
  "b": {
    "g": "v",
    "h": {
      "l": "ab",
      "k": {
        "m": "x",
        "n": "y",
        "o": "z",
        "p": "aa"
      }
    }
  }
} 

这似乎与所需的输出相匹配。

此函数强烈假设“值”列始终是最后一列,并且其之前的所有列都将被解释为名称。

© www.soinside.com 2019 - 2024. All rights reserved.