Use dput and capture.output to get code you can run to recreate an object.
text <- capture.output(dput(df))
# capture.output splits lines like they'd print, so combine them
text <- paste0(text, collapse = "")
text
# [1] "list(structure(list(a = structure(list(a = \"some text\", b = \"more text\"),
# row.names = c(NA, -1L), class = c(\"tbl_df\", \"tbl\", \"data.frame\")), b = \"char\"),
# row.names = c(NA, -1L), class = c(\"tbl_df\", \"tbl\", \"data.frame\")))"
as.character converts an object's value(s) into whatever text the object's developer thought appropriate. In the case of lists, it looks like code you'd write. And a tibble (like a data.frame) is actually a list that can do a few more tricks. So, as.character treats it like a list.
But, for example, factors are converted to their levels. You can't always recreate them from what as.character gives.