2016-12-14 2 views
2

Я пишу ящик, который взаимодействует с веб-интерфейсом JSON. Одна из конечных точек обычно возвращают ответы вида { "key": ["value1", "value2"] }, но иногда есть только одно значение для ключа, а конечная точка возвращает { "key": "value" } вместо { "key": ["value"] }Deserialize строка JSON или массив строк в Vec

Я хотел написать что-то общее для этого, что я мог бы использовать с #[serde(deserialize_with)] так:

#[derive(Deserialize)] 
struct SomeStruct { 
    #[serde(deserialize_with = "deserialize_string_or_seq_string")] 
    field1: Vec<SomeStringNewType>, 

    #[serde(deserialize_with = "deserialize_string_or_seq_string")] 
    field2: Vec<SomeTypeWithCustomDeserializeFromStr>, 
} 

#[derive(Deserialize)] 
struct SomeStringNewType(String); 

struct SomeTypeWithCustomDeserializeFromStr(String); 
impl ::serde::de::Deserialize for SomeTypeWithCustomDeserializeFromStr { 
    // Some custom implementation here 
} 

Как написать deserialize_string_or_seq_string, чтобы иметь возможность это сделать?

ответ

0

Это решение работает для Serde 1.0.

Как я нашел, мне также потребовалось написать собственный десериализатор, потому что мне нужен был тот, который вызвал бы visitor.visit_newtype_struct, чтобы попробовать десериализовать newtypes, и, похоже, нет встроенных в serde, которые делают это. (Я ожидал чего-то вроде серии типов .)

Ниже приведен личный пример. SomeStruct десериализуется правильно для обоих входов, где значения представляют собой массивы JSON строк, а остальные - только строки.

#[macro_use] 
extern crate serde; 
#[macro_use] 
extern crate serde_derive; 
extern crate serde_json; 

fn main() { 
    #[derive(Debug, Deserialize)] 
    struct SomeStringNewType(String); 

    #[derive(Debug)] 
    struct SomeTypeWithCustomDeserializeFromStr(String); 
    impl<'de> ::serde::Deserialize<'de> for SomeTypeWithCustomDeserializeFromStr { 
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'de> { 
      struct Visitor; 

      impl<'de> ::serde::de::Visitor<'de> for Visitor { 
       type Value = SomeTypeWithCustomDeserializeFromStr; 

       fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 
        write!(f, "a string") 
       } 

       fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: ::serde::de::Error { 
        Ok(SomeTypeWithCustomDeserializeFromStr(v.to_string() + " custom")) 
       } 
      } 

      deserializer.deserialize_any(Visitor) 
     } 
    } 

    #[derive(Debug, Deserialize)] 
    struct SomeStruct { 
     #[serde(deserialize_with = "deserialize_string_or_seq_string")] 
     field1: Vec<SomeStringNewType>, 

     #[serde(deserialize_with = "deserialize_string_or_seq_string")] 
     field2: Vec<SomeTypeWithCustomDeserializeFromStr>, 
    } 

    let x: SomeStruct = ::serde_json::from_str(r#"{ "field1": ["a"], "field2": ["b"] }"#).unwrap(); 
    println!("{:?}", x); 
    assert_eq!(x.field1[0].0, "a"); 
    assert_eq!(x.field2[0].0, "b custom"); 

    let x: SomeStruct = ::serde_json::from_str(r#"{ "field1": "c", "field2": "d" }"#).unwrap(); 
    println!("{:?}", x); 
    assert_eq!(x.field1[0].0, "c"); 
    assert_eq!(x.field2[0].0, "d custom"); 
} 

/// Deserializes a string or a sequence of strings into a vector of the target type. 
pub fn deserialize_string_or_seq_string<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error> 
    where T: ::serde::Deserialize<'de>, D: ::serde::Deserializer<'de> { 

    struct Visitor<T>(::std::marker::PhantomData<T>); 

    impl<'de, T> ::serde::de::Visitor<'de> for Visitor<T> 
     where T: ::serde::Deserialize<'de> { 

     type Value = Vec<T>; 

     fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 
      write!(f, "a string or sequence of strings") 
     } 

     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 
      where E: ::serde::de::Error { 

      let value = { 
       // Try parsing as a newtype 
       let deserializer = StringNewTypeStructDeserializer(v, ::std::marker::PhantomData); 
       ::serde::Deserialize::deserialize(deserializer) 
      }.or_else(|_: E| { 
       // Try parsing as a str 
       let deserializer = ::serde::de::IntoDeserializer::into_deserializer(v); 
       ::serde::Deserialize::deserialize(deserializer) 
      })?; 
      Ok(vec![value]) 
     } 

     fn visit_seq<A>(self, visitor: A) -> Result<Self::Value, A::Error> 
      where A: ::serde::de::SeqAccess<'de> { 

      ::serde::Deserialize::deserialize(::serde::de::value::SeqAccessDeserializer::new(visitor)) 
     } 
    } 

    deserializer.deserialize_any(Visitor(::std::marker::PhantomData)) 
} 

// Tries to deserialize the given string as a newtype 
struct StringNewTypeStructDeserializer<'a, E>(&'a str, ::std::marker::PhantomData<E>); 

impl<'de, 'a, E> ::serde::Deserializer<'de> for StringNewTypeStructDeserializer<'a, E> where E: ::serde::de::Error { 
    type Error = E; 

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: ::serde::de::Visitor<'de> { 
     visitor.visit_newtype_struct(self) 
    } 

    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: ::serde::de::Visitor<'de> { 
     // Called by newtype visitor 
     visitor.visit_str(self.0) 
    } 

    forward_to_deserialize_any! { 
     bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str bytes 
     byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map 
     struct enum identifier ignored_any 
    } 
} 
2

В случае, если вы хотите десериализации одну строку или список строк в более общем Vec<String> вместо пользовательского типа, следующее простое решение для Serde 1.0:

extern crate serde; 
#[macro_use] extern crate serde_derive; 
extern crate serde_json; 

use std::fmt; 
use std::marker::PhantomData; 

use serde::de; 
use serde::de::{Deserialize, Deserializer}; 

#[derive(Deserialize, Debug, Clone)] 
pub struct Parent { 
    #[serde(deserialize_with = "string_or_seq_string")] 
    pub strings: Vec<String>, 
} 

fn main() { 
    let list_of_strings: Parent = serde_json::from_str(r#"{ "strings": ["value1", "value2"] }"#).unwrap(); 
    println!("list of strings: {:?}", list_of_strings); 
    // Prints: 
    // list of strings: Parent { strings: ["value1", "value2"] } 

    let single_string: Parent = serde_json::from_str(r#"{ "strings": "value" }"#).unwrap(); 
    println!("single string: {:?}", single_string); 
    // Prints: 
    // single string: Parent { strings: ["value"] } 
} 

fn string_or_seq_string<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error> 
    where D: Deserializer<'de> 
{ 
    struct StringOrVec(PhantomData<Vec<String>>); 

    impl<'de> de::Visitor<'de> for StringOrVec { 
     type Value = Vec<String>; 

     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 
      formatter.write_str("string or list of strings") 
     } 

     fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> 
      where E: de::Error 
     { 
      Ok(vec![value.to_owned()]) 
     } 

     fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error> 
      where S: de::SeqAccess<'de> 
     { 
      Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor)) 
     } 
    } 

    deserializer.deserialize_any(StringOrVec(PhantomData)) 
} 

Это решение также работает под 0,9 выпуском Serde со следующими изменениями:

  • удалить времена жизни
  • ->SeqVisitor
  • SeqAccessDeserializer ->SeqVisitorDeserializer
  • MapAccess ->MapVisitor
  • MapAccessDeserializer ->MapVisitorDeserializer
+0

Это не десериализации 'SomeStringNewType (String)' или 'SomeTypeWithCustomDeserializeFromStr (String)' как вопрос требует. – Arnavion

+1

Извините, я понял, что 'SomeStringNewType' был способом обхода, потому что вы не могли заставить его работать с' String'. Если вы не возражаете, я все равно буду отвечать на этот вопрос, потому что, по крайней мере, я столкнулся с этим вопросом, потому что искал его название. Я обновил ответ, чтобы указать, что это для общей формы желания простого 'String'. – Pit

+1

Да, ваш ответ подходит для случая, когда пользователь хочет десериализовать строку напрямую. Нет причин для его удаления. – Arnavion

Смежные вопросы