Как я могу разобрать массив JSON строк или объектов?

API, который я использовал, имеет плохо структурированный JSON. Кто-то решил, что отправить список, который выглядит так:

features: [
  "First one",
  "second one",
  {
    "feature": "third one",
    "hasAdditionalImpact": true
  },
  "forth one"
]

Я придумал способ получить эти данные в структуре, но это было эффективно:

struct MyStruct {
    SensibleData: String,
    SensibleTruthy: bool,
    features: serde_json::Value,
}

Это не помогает мне нормализовать и проверять данные.

Есть ли хороший способ превратить этот первый объект во что-то вроде

features: [
  {
    "feature": "First one",
    "hasAdditionalImpact": false
  },
  {
    "feature": "second one",
    "hasAdditonalImpact": false
  },
  {
    "feature": "third one",
    "hasAdditionalImpact": true
  },
  {
    "feature": "forth one",
    "hasAdditionalImpact": false
  }
]

Я видел, что type_name можно использовать для проверки типа и выполнения постобработка после его анализа serde_json, но я также видел, что type_name предназначен для диагностических целей, поэтому я бы предпочел не использовать его для этой цели.


person ngin    schedule 02.07.2021    source источник
comment
Похоже, на ваш вопрос могут ответить ответы Как условно десериализовать JSON в два разных варианта перечисления?; Преобразуйте два типа в один с помощью Serde; Как десериализовать JSON, когда типы значений указаны в другом поле?. В противном случае отредактируйте свой вопрос, чтобы объяснить различия. В противном случае мы можем отметить этот вопрос как уже полученный.   -  person Shepmaster    schedule 02.07.2021
comment
См. Также Rust serde десериализация смешанного массива; Десериализация массива JSON, содержащего различные типы объектов. Действительно, случайный поиск дает довольно много результатов.   -  person Shepmaster    schedule 03.07.2021


Ответы (1)


Похоже, что функции вашего JSON имеют две формы; явный объект и упрощенная форма, в которой некоторые поля имеют значения по умолчанию или безымянные. Вы можете смоделировать это с помощью eum следующим образом:

#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum Feature {
    Simple(String),
    Explicit {
        feature: String,
        #[serde(rename = "hasAdditionalImpact")]
        has_additional_impact: bool,
    }
}

(детская площадка)

Атрибут #[serde(untagged)] означает, что он будет пытаться десериализовать каждый вариант по порядку, пока один из них не будет успешным.


Если перечисление будет раздражать, вы можете преобразовать их все в одну структуру со значениями по умолчанию, используя #[serde(from)] и предоставив преобразование From:

#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum FeatureSource {
    Simple(String),
    Explicit {
        feature: String,
        #[serde(rename = "hasAdditionalImpact")]
        has_additional_impact: bool,
    },
}

#[derive(Deserialize, Debug)]
#[serde(from = "FeatureSource")]
struct Feature {
    feature: String,
    has_additional_impact: bool,
}

impl From<FeatureSource> for Feature {
    fn from(other: FeatureSource) -> Feature {
        match other {
            FeatureSource::Simple(feature) => Feature {
                feature,
                has_additional_impact: false,
            },
            FeatureSource::Explicit {
                feature,
                has_additional_impact,
            } => Feature {
                feature,
                has_additional_impact,
            },
        }
    }
}

(площадка)

FeatureSource используется только как промежуточное представление и преобразуется в Feature прежде, чем остальная часть вашего кода когда-либо увидит его.

person Peter Hall    schedule 02.07.2021
comment
Извините, что мне потребовалось немного времени, чтобы ответить; большое спасибо! Это круто. Второй вариант идеален, и я могу использовать его, чтобы экстраполировать то, что мне нужно сделать для остальной части этого странного синтаксического анализа для этого проекта! Я принял и проголосовал за ваш ответ. Ты замечательный! - person ngin; 05.07.2021