Reputation: 15016
Until last night, I've been parsing XML using a variety of libraries in .NET -- XmlDocument
and XDocument
mostly. I'm not sure why I didn't look into this sooner, but it occurred to me that there must be something available in .NET that gives you class serialization / deserialization for free, and of course that comes in the form of the XmlSerializer
class.
Sure enough, by using a few lines of code, I was able to serialize and deserialize with ease (although in the code I'm currently writing, I only need to deserialize), and no longer had to take the few hours or so to write my own class to do this with other libraries, plus the requisite unit tests. But the problem is that I would like my properties to be read-only. If I make the setter private, then upon creation of the XmlSerializer I get this error:
Unable to generate a temporary class (result=1). error CS0200: Property or indexer 'MyProperty' cannot be assigned to -- it is read only
It looks like this is an issue that won't be resolved, so there must be a workaround.
Sure enough, I found this information, which indicates that you can get the code to compile if you give up auto properties and just back with private fields. Unfortunately, while this compiles, when you execute the code, it doesn't actually deserialize the data. After stopping my application, I noticed several entries in the Messages window that said this:
Could not find schema information for the element 'MyProperty'.
And this is because there's no code to assign a value to MyProperty, because XmlSerializer doesn't know how to deal with private fields!!!
I found an answer on StackOverflow that presents another solution, which is to use a DataContractSerializer
which I hadn't heard of before. I made the necesary code changes to my class, but ended up with the same messages as above. I ran the code to be sure, and the class members don't get set when the XML is deserialized.
I'm thinking that in my particular case, I either suck it up and allow the members to get overwritten (bad), or I go back to my original way of doing things, which is to just write all of the serialization / deserialization code myself. Is there something I'm doing wrong here, or is it impossible to allow a class like XmlSerializer to set private members of a class during deserialization, while making the consumer of the class not be able to overwrite its members?
UPDATE: and yet another article that shows another way to do deserialization of private properties, but I just tried it and it also doesn't work.
Here are some examples of the class that I've tried to deserialize:
[Export]
[DataContract]
public class Configuration
{
[DataMember(Name="Port")]
private int _port;
public int Port { get { return _port; }}
}
Result: when deserializing with the XmlSerializer, there are no errors, but _port and Port have a value of 0 when my XML file has a Port value of 1, e.g. <Port>1</Port>
.
Another example:
[Export]
public class Configuration
{
public int Port { get; set; }
}
Result: deserializes fine, but I don't want a public setter.
I deserialize the class like this:
XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
FileStream reader = new FileStream( "config.xml", FileMode.Open);
Configuration Config = (Configuration)serializer.Deserialize( reader);
reader.Close();
Upvotes: 2
Views: 2091
Reputation: 15016
I think, ultimately, that the correct answer to this question is that if you need to deserialize (serialization isn't an issue) private data, then you want to avoid using XmlSerializer and DataContractSerializer. I still hope that I'm wrong, but in the end, Jon might be right -- I will need to either use ISerializable, or do it the way I've done it up until now.
I hit another brick wall tonight when I decided to use one class with public properties to allow deserialization, and then wrap it with another class. I didn't want to make the deserializable class public -- I wanted it to be a private class within the one that only exposes readonly properties. Well, the issue with that is that XmlSerializable can't deal with classes that are private.
I could just make the class public for now, which I might do just to get things working first, but I'm going to go a different route because having to make that class public just doesn't make sense.
Upvotes: 0
Reputation: 113352
This isn't a question of which XML serialisation method to use, but about the serialisation itself (try it with a binary serialisation, and you'll get the same error).
If you cannot change the property to allow the default serialisation method to take place, then:
Now, this may seem a bit like going back to manual serialisation, however it need only be done on the classes in quesiton, and it also makes your class serialisable by other mechanisms.
Upvotes: 1
Reputation: 19630
I don't have a good answer, but I have several bad ones. In no particular order:
You can create a pure data transfer object with r/w properties and use it for serialization/deserialization. You can then initialize your immutable business object by constructing with the DTO.
The two serializer classes you mentioned also allow overriding their behavior through a combination of attributes and code. This is likely sufficient to solve your problem, but may well be more work than it's worth.
Upvotes: 3