Разобрать строку JSON внутри другого JSON

У меня есть некоторый JSON, который содержит другой объект JSON внутри в виде строки (обратите внимание на кавычки вокруг значения "jsonString"):

{
  "jsonString":"{\"someKey\": \"Some value\"}"
}

Я знаю, что если бы значение "jsonString" не заключалось в кавычки, я бы сделал что-то вроде этого:

import Foundation

struct Something: Decodable {
  struct SomethingElse: Decodable {
    let someKey: String
  }
  let jsonString: SomethingElse
}

let jsonData = """
               {
                 "jsonString":"{\"someKey\": \"Some value\"}"
               }
               """.data(using: .utf8)!
let something = try! JSONDecoder().decode(Something.self, from: jsonData)

Но это не работает для моего случая. Это не сработает, даже если я буду рассматривать "jsonString" как String и делать что-то вроде этого:

init(from decoder: Decoder) throws {
  let container = try decoder.container(keyedBy: SomethingKey.self)

  let jsonStringString = try container.decode(String.self, forKey: .jsonString)
  if let jsonStringData = jsonStringString.data(using: .utf8) {
    self.jsonString = try JSONDecoder().decode(SomethingElse.self, from: jsonStringData)
  } else {
    self.jsonString = SomethingElse(someKey: "")
  }
}
private enum SomethingKey: String, CodingKey {
  case jsonString
}

Ошибка, которую я испытываю:

Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Badly formed object around line 2, column 18." UserInfo = {NSDebugDescription=Badly formed object around line 2, column 18., NSJSONSerializationErrorIndex=20})))

Однако все валидаторы JSON, которые я пробовал, говорят, что JSON действителен и соответствует RFC 8259.

Swift также не позволяет мне избежать вложенных "{" и "}".


К сожалению, формат JSON находится вне моего контроля, и я не могу его изменить.

Я также нашел вопрос это, который выглядит похожим, но там ничего не работает. Ответы актуальны только для обычных вложенных объектов JSON. Или я что-то пропустил.

Любая помощь приветствуется!

На мой взгляд, это не похоже на действительный JSON!

phuzi 10.05.2022 14:23

JSON должен быть {"jsonString": "{\"someKey\": \"someValue\"}"}, исключая двойные кавычки внутри значения jsonString. Указанный недействителен. В своем пользовательском init(from decoder:) не могли бы вы напечатать let jsonString?

Larme 10.05.2022 14:23

Ошибка говорит о том, что JSON вообще недействителен, а не часть try container.decode(String.self, forKey: .jsonString), JSON Stringified с JSON.

Larme 10.05.2022 14:24

@phuzi Я обновил вопрос, немного переформатировал и дополнил некоторыми деталями. JSON действителен.

lazarevzubov 10.05.2022 14:35

@Larme Я обновил вопрос, немного переформатировал и дополнил некоторыми деталями. JSON действителен. init(from decoder: Decoder) даже не вызывается, ошибка вылетает на строку JSONDecoder().decode(Something.self, from: jsonData). Пожалуйста, попробуйте вставить мой фрагмент кода в Playground, и вы сразу поймете, что я имею в виду.

lazarevzubov 10.05.2022 14:37

Это потому, что в jsonData, которое вы дали, оно плохо создано. это либо: let jsonData = """--{"jsonString":"{\\"someKey\\": \\"Some value\\"}"}---""".data(using: .utf8)!, где "--" — это новая строка в среде IDE, либо let jsonData = #"{"jsonString":"{\"someKey\": \"Some value\"}"}"#.data(using: .utf8)!, поскольку при объявлении строки вам все равно нужно экранировать обратную косую черту.

Larme 10.05.2022 14:42

@Ларме Гений! Оба варианта сработали, как и ожидалось, большое спасибо! Если вы опубликуете свою находку в качестве ответа, я с радостью отмечу ее как принятую. ;)

lazarevzubov 10.05.2022 14:46

(Еще одна вещь, о которой стоит упомянуть, это то, что я не знал, что не могу добавлять разрывы строк внутри этой вложенной строки JSON.)

lazarevzubov 10.05.2022 14:52
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
8
40
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ошибка говорит о том, что весь JSON недействителен, а не только значение jsonString.

Хотя это правда (вы можете проверить это в онлайн-валидаторе, например, JSONLint):

{
    "jsonString": "{\"someKey\": \"Some value\"}"
}

При объявлении его как строки в Swift вам нужно убедиться, что обратная косая черта действительно присутствует.

Так что это:

let jsonString = """
{ "jsonString": "{\\"someKey\\": \\"Some value\\"}"}
"""

или

let jsonString = #"{"jsonString":"{\"someKey\": \"Some value\"}"}"#

Другие вопросы по теме