:doing_it_wrong: with DataContractSerializer



  • So: We have some code that gets serialized and de-serialized outside of our control, and it seems to use DataContractSerializer. The problem is, we are under direct instruction to make our system accept bad data, as long as it isn't typed wrong, and therein lies the problem: Some of our fields are enums.

    The reason this is a problem is that the DataContractSerializer serializes enums as strings: Understandable since it was designed explicitly to allow data transfer between different applications using XML, regardless of what integer values back the enums. But we aren't using it for that: we are using it as the integer value. The serializer was also designed to ensure that data contract does not change, and is a proper contract; in our case, we explicitly allow invalid values.

    So right now, things are broken. We can't retrieve some objects from the data store. They're strings in there, and we don't control the call to the deserialization, so we can't mess with it directly.

    Anyone have any ideas?


  • Discourse touched me in a no-no place

    @Magus said in :doing_it_wrong: with DataContractSerializer:

    The serializer was also designed to ensure that data contract does not change, and is a proper contract; in our case, we explicitly allow invalid values.

    It sounds like they're only sort-of-wish-they-were-enums, not real ones. Which means you need (effectively) a union type of enum and string there, with the latter catching all cases not recognised as members of the enum so that you can at least transport them correctly. You will very likely need some sort of wrapping scheme too; I know how I'd handle it in Java (interface, enum and string-container that both implement the interface, and a factory somewhere to make the right one for the deserializer) but that isn't necessarily helpful for C#. If you're unlucky, you may just need to use strings and recognise some as special cases. Wheee!

    Also, this sort of data problem is annoyingly common with real data. It's often a sign that someone has got the analysis of the problem space wrong, and that either there's a lot of elements missing from the enum or there never should have been one in the first place and that something more string-like is the correct base model. (This gets doubly fun with a spec like RDF, where normal apps can only cope with what's effectively an enum, but where the spec says “general arbitrary URIs, suckers” so you're always potentially subject to random extra shit you can't handle. I really don't like RDF.)



  • @dkf Yeah... if we had just stored them as the ints we receive them as, we'd be fine... Turning them into enumerations was meant to help us keep the types consistent, but that doesn't work when you're secretly using two serializers for different parts of the app.



  • If you're already stuck with having to have those fields be strings in the data store, the only option I can think of is that you add a String property that becomes the actual value to serialize/deserialize in place of the Enum, and make the Enum property try to coerce the string value on first access.

    Or, as an example, albeit a bit shitty one:

    [DataContract()]
    class Sample
    {
    	private String _valueCompat;
    	[DataMember(Name = "value")] public String valueCompat
    	{
    		get { if (value != SampleEnum.UnknownValue) { return value.ToString(); } else { return _valueCompat; } }
    		set { _valueCompat = value; }
    	}
    	private SampleEnum _value;
    	[IgnoreDataMember()] public SampleEnum value
    	{
    		get { 
    			if (_value == SampleEnum.UnknownValue && !String.IsNullOrWhiteSpace(_valueCompat))
    			{
    				if (!Enum.TryParse<SampleEnum>(_valueCompat, out _value)) _value = SampleEnum.UnknownValue;
    			}
    			return _value;
    		}
    		set
    		{
    			_value = value;
    		}
    	}
    	//Other parameters/methods/etc...
    }
    
    enum SampleEnum : uint
    {
    	UnknownValue = 0,
    	FirstValue = 1,
    	SecondValue = 2,
    	ThirdValue = 3
    }
    

    That way, you should be able to pull from the data store, and even put said bad data back in (if it wouldn't be changed between being read and written back), but it shouldn't break your code, though you'd need to accommodate the "UnknownValue" enum item as 0 (since DataContractSerializer doesn't call a constructor).



  • If you want integers....

    [Serializable]
    public enum EnumToSerialize
    {
        [XmlEnum("1")]
        One = 1,
        [XmlEnum("2")]
        Two = 2
    }
    


  • @TheCPUWizard

    1. DataContractSerializer
    2. Already stored as strings because of datacontractserializer
    3. Still doesn't solve accepting unrecognized values, which are valid for enums, but not that sort of serialization.


  • Change your application to use strings where you currently use enums? Not pretty, but it lets you accept strings.

    Alternatively: change the enums to classes and add implicit operators to convert between your custom type and strings. It'll break switch-statements but it'll work otherwise.



  • @AlexMedia Again, that would be the opposite of what we want. We're implementing a government spec that uses integers.



  • @Magus said in :doing_it_wrong: with DataContractSerializer:

    @dkf Yeah... if we had just stored them as the ints we receive them as, we'd be fine... Turning them into enumerations was meant to help us keep the types consistent, but that doesn't work when you're secretly using two serializers for different parts of the app.

    So where did this go? Can you undo it and go back to storing integers? Keep the enum off to the side for comparisons, but if the spec says something is an integer and values outside the ones with meaning are allowed and must be preserved, then it's not an enumerated type. Unfortunately.

    (This sounds a lot like a configuration file I had to wrangle once that had 23 different ways to say True or False, all of which had to be preserved in context.)



  • @Parody Yeah, so, this is data we've had distributed through the state storage on a bunch of service fabric actors for a few months. I have yet to get to trying to address this, because it is depressing and not likely to go well.


Log in to reply