2017-10-15 20 views
4

J'utilise Serde désérialiser un fichier XML qui a la valeur hexadécimale 0x400 comme une chaîne et je dois le convertir à la valeur 1024 comme u32.Comment transformer des champs lors de la désérialisation en utilisant Serde?

Ai-je besoin de mettre en œuvre le trait Visitor pour que je puis séparer 0x décoder 400 de la base 16 à la base 10? Si oui, comment puis-je faire pour que la désérialisation des entiers de la base 10 reste intacte?

Répondre

5

La solution la plus simple consiste à utiliser le Serde field attributedeserialize_with pour définir une fonction de sérialisation personnalisée pour votre champ. Vous pouvez ensuite obtenir la chaîne brute et convert it as appropriate:

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

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

#[derive(Debug, Deserialize)] 
struct EtheriumTransaction { 
    #[serde(deserialize_with = "from_hex")] 
    account: u64, // hex 
    amount: u64, // decimal 
} 

fn from_hex<'de, D>(deserializer: D) -> Result<u64, D::Error> 
where 
    D: Deserializer<'de>, 
{ 
    let s: &str = Deserialize::deserialize(deserializer)?; 
    // do better hex decoding than this 
    u64::from_str_radix(&s[2..], 16).map_err(D::Error::custom) 
} 

fn main() { 
    let raw = r#"{"account": "0xDEADBEEF", "amount": 100}"#; 
    let transaction: EtheriumTransaction = 
     serde_json::from_str(raw).expect("Couldn't derserialize"); 
    assert_eq!(transaction.amount, 100); 
    assert_eq!(transaction.account, 0xDEAD_BEEF); 
} 

playground

De là, il est un petit pas à la promotion à votre propre type pour permettre sa réutilisation:

#[derive(Debug, Deserialize)] 
struct EtheriumTransaction { 
    account: Account, // hex 
    amount: u64,  // decimal 
} 

#[derive(Debug, PartialEq)] 
struct Account(u64); 

impl<'de> Deserialize<'de> for Account { 
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 
    where 
     D: Deserializer<'de>, 
    { 
     let s: &str = Deserialize::deserialize(deserializer)?; 
     // do better hex decoding than this 
     u64::from_str_radix(&s[2..], 16) 
      .map(Account) 
      .map_err(D::Error::custom) 
    } 
} 

playground

Voir aussi:

+0

Merci @Shepmaster. C'est exactement ce que je cherche. Le seul problème est la référence à la chaîne que je reçois cette panique msg: type non valide: chaîne « 0x400 », attend une chaîne empruntée. – phodina

+0

@phodina Mes deux exemples exécuter avec succès, si évidemment vous avez changé quelque chose. Je ne suis pas un lecteur d'esprit, donc je ne sais pas quelle est cette différence. Vous pouvez essayer 'let s: String = ...' dans le cas où le décodeur XML ne peut pas fournir de tranches de chaîne. – Shepmaster

+0

J'ai essayé vos deux exemples dans le terrain de jeu et sur mon PC et ils fonctionnent tous les deux. Le problème survient lorsque je change le format sous-jacent de json en xml. Voici le code (terrain de jeu manque la caisse serde_xml_rs) let raw = r # " 0xDEADBEEF" #; transaction let. EtheriumTransaction = serde_xml_rs :: deserialize (raw.as_bytes()) déroulez(); – phodina