2009-07-24 8 views
9

J'ai une structure en C# qui enveloppe un guid. J'utilise DataContractJsonSerializer pour sérialiser un objet contenant une instance de cette classe. Lorsque j'utilisais un guid directement, il était sérialisé en tant que chaîne simple, mais maintenant il est sérialisé en tant que paire nom/valeur. Voici un test NUnit et le code de support qui illustre le problème:Comment puis-je faire en sorte que DataContractJsonSerializer sérialise un objet en tant que chaîne?

private static string ToJson<T>(T data) 
    { 
     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof (T)); 

     using (MemoryStream ms = new MemoryStream()) 
     { 
      serializer.WriteObject(ms, data); 
      return Encoding.Default.GetString(ms.ToArray()); 
     } 
    } 

    [Serializable] 
    private class ID 
    { 
     private Guid _value; 

     public static explicit operator ID(Guid id) 
     { 
      return new ID { _value = id }; 
     } 

     public static explicit operator Guid(ID id) 
     { 
      return id._value; 
     } 
    } 

    [Test] 
    public void IDShouldSerializeLikeGuid() 
    { 
     Guid guid = Guid.NewGuid(); 
     ID id = (ID) guid; 
     Assert.That(ToJson(id), Is.EqualTo(ToJson(guid))); 
    } 

Et la sortie du lanceur de test:

NUnit.Framework.AssertionException: Expected string length 38 but was 49. Strings differ at index 0. 
    Expected: ""7511fb9f-3515-4e95-9a04-06580753527d"" 
    But was: "{"_value":"7511fb9f-3515-4e95-9a04-06580753527d"}" 
    -----------^ 

Comment puis-je sérialiser mon struct comme une chaîne simple et faire mon test passe?

Répondre

10

Dans ce cas, il semble que vous ne voulez pas vraiment JSON, vous voulez une représentation sous forme de chaîne. Dans ce cas, je créerais une interface comme ceci:

interface IStringSerialized 
{ 
    String GetString(); 
} 

implémentent cette interface sur votre ID de type (et tous les autres types qui ont des exigences similaires).

[Serializable] 
class ID : IStringSerialized 
{ 
    private Guid _value; 

    public static explicit operator ID(Guid id) 
    { 
     return new ID { _value = id }; 
    } 

    public static explicit operator Guid(ID id) 
    { 
     return id._value; 
    } 

    public string GetString() 
    { 
     return this._value.ToString(); 
    } 
} 

Modifiez ensuite votre méthode de sérialisation pour traiter ces cas particuliers:

private static string ToJson<T>(T data) 
{ 
    IStringSerialized s = data as IStringSerialized; 

    if (s != null) 
     return s.GetString(); 

    DataContractJsonSerializer serializer 
       = new DataContractJsonSerializer(typeof(T)); 

    using (MemoryStream ms = new MemoryStream()) 
    { 
     serializer.WriteObject(ms, data); 
     return Encoding.Default.GetString(ms.ToArray()); 
    } 
} 
+0

J'ai omis une certaine complexité pour rendre le problème clair, mais dans l'application réelle, c'est dans le contexte d'un objet contenant qui contient une liste . Je veux que le vrai code produise ["guid1", "guid2"], pas [{"_value": "guid1"}, {"_value": "guid2"}]. Alors malheureusement, cette approche ne fonctionnera pas pour moi. Merci pour l'idée cependant! –

+7

N'utilisez pas l'encodage par défaut car cela endommagerait les caractères non-ANSI. Je fais actuellement face à des messages push de téléphone portable. Votre code devrait pouvoir traiter avec d'autres langues. Utilisez à la place, UTF8. Renvoie Encoding.UTF8.GetString (ms.ToArray()); – midspace

0

Essayez d'utiliser la classe JavaScriptSerializer il empêchera la clé, la valeur problème de ballonnement.

+1

mais alors vous perdrez tous les attributs de contrat de données que vous avez dans votre objet que vous souhaitez sérialiser (comme emitdefaultvalue) – silver

Questions connexes