I need to serialize IEnumerable. At the same time I want root node to be "Channels" and second level node - Channel (instead of ChannelConfiguration).
Here is my serializer definition:
_xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>), new XmlRootAttribute("Channels"));
I have overriden root node by providing XmlRootAttribute but I haven't found an option to set Channel instead of ChannelConfiguration as second level node.
I know I can do it by introducing a wrapper for IEnumerable and using XmlArrayItem but I don't want to do it.
Like so:
XmlAttributeOverrides or = new XmlAttributeOverrides();
or.Add(typeof(ChannelConfiguration), new XmlAttributes
{
XmlType = new XmlTypeAttribute("Channel")
});
var xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>), or,
Type.EmptyTypes, new XmlRootAttribute("Channels"), "");
xmlSerializer.Serialize(Console.Out,
new List<ChannelConfiguration> { new ChannelConfiguration { } });
Note you must cache and re-use this serializer instance.
I will also say that I strongly recommend you use the "wrapper class" approach - simpler, no risk of assembly leakage, and IIRC it works on more platforms (pretty sure I've seen an edge-case where the above behaves differently on some implementations - SL or WP7 or something like that).
If you have access to the type ChannelConfiguration, you can also just use:
[XmlType("Channel")]
public class ChannelConfiguration
{...}
var xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>),
new XmlRootAttribute("Channels"));
xmlSerializer.Serialize(Console.Out,
new List<ChannelConfiguration> { new ChannelConfiguration { } });
This should do the trick, if I remember correctly.
[XmlType("Channel")]
public class ChannelConfiguration {
}
Related
I have a dictionary of abilityobjects <id, abilityObj> that I'm trying to serialize in XML. Because you can't XML Serialize a dictionary, I change it into a list on serialization
public class BattleSerializable : TyrantSerializable
{
[XmlIgnoreAttribute]
[NonSerialized]
[DoNotSerialize]
public Dictionary<int, AbilityObjectSerializable> battleObjects;
public List<AbilityObjectSerializable> serializedBattleObjects
{
get
{
if (battleObjects == null)
{
battleObjects = new Dictionary<int, AbilityObjectSerializable>();
}
return battleObjects.Select(x => x.Value).ToList();
}
set
{
battleObjects = value.ToDictionary(x => x.entityId, x => x);
}
}
It serializes correctly. I.e. the XML that gets saved makes sense
<BattleSerializable>
...
<serializedBattleObjects>
<AbilityObjectSerializable xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" d3p1:type="FireballObject">
<hexDirection>southeast</hexDirection>
<gridX>0</gridX>
<gridZ>7</gridZ>
<entityId>3</entityId>
<lastAnimation>STATUE</lastAnimation>
<timer>0</timer>
<abilityPos>2</abilityPos>
<abilityType>FIREBALL</abilityType>
<health>100</health>
<tilesPerTurn>2</tilesPerTurn>
<jump>1</jump>
<fall>99</fall>
<damage>5</damage>
<lineTraversed>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</lineTraversed>
<moveDirection>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</moveDirection>
</AbilityObjectSerializable>
</serializedBattleObjects>
</BattleSerializable>
But when I try to then -load- this XML and turn it into actual C# objects, this list comes in empty for some reason, causing the app to blow up.
What am I missing? All the other lists in this class serialize/deserialize correctly.
My load code:
public BattleSerializable Load(string path)
{
var serializer = new XmlSerializer(typeof(BattleSerializable));
try
{
using (var stream = new FileStream(path, FileMode.Open))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(stream);
string xmlString = xmlDoc.InnerXml;
BattleSerializable bs = (BattleSerializable)this.LoadFromXML(xmlString);
return bs;
}
}
catch (Exception e)
{
throw new SettingLoadException("Settings failed validation");
}
}
The way a lot of serializers work is by calling Add on a list, only actually assigning anything back to the setter if the serializer created the list (perhaps because it was null, or fixed size such as an array). So imagine the serializer doing:
var list = obj.SomeProperty;
while (moreOfTheSame)
list.Add(ReadOneOfThose());
It never calls the setter, so any logic in there: irrelevant. You'll probably need a custom list type here, or perhaps more simply: have a nice simple POCO/DTO model that just maps to the serialization shape with no fun logic, and project between this model and your domain model separately to serialization.
I have implemented my XmlTextReader with overridden setting for CheckCharacters. Something like this:
class MyXmlTextReader : XmlTextReader
{
public MyXmlTextReader(TextReader input) : base(input)
{
}
/// <summary>
/// Settings
/// </summary>
public override XmlReaderSettings Settings
{
get { return new XmlReaderSettings { CheckCharacters = false }; }
}
}
When I use it in normal scenario with invalid xml data everything works fine:
var sr3 = new StringReader(xml);
var xr3 = new MyXmlTextReader(sr3);
var obj3 = (MyObject)ser.Deserialize(xr3);
But as soon as I turn on normalisation, I start getting InvalidCharacter exceptions:
var sr3 = new StringReader(xml);
var xr3 = new MyXmlTextReader(sr3);
xr3.Normalization = true;
var obj3 = (MyObject)ser.Deserialize(xr3);
Is there a way to have normalisation, but at the same time ignore invalid xml characters?
Here is a sample application to reproduce the problem:
https://gist.github.com/ncksol/29bd6490edd0580c25f7338b417b37d3
This appears to be a shortcoming in the implementation:
XmlReader has no Normalization property.
XmlReader.Create allows you to pass CheckCharacters as a setting, but since it returns XmlReader, you can't control the normalization through it.
XmlTextReader (actually wrapping XmlTextReaderImpl) has Normalization, but no public CheckCharacters property, and no way of accepting XmlReaderSettings.
Finally, XmlTextReaderImpl, which does all the real work, can do both normalization and omitted character checking, but due to all of the above, there is no public path to configuring it that way.
If you don't mind relying on the implementation in this case, it can be done through reflection:
var sr3 = new StringReader(xml);
var xr3 = XmlReader.Create(sr3, new XmlReaderSettings { CheckCharacters = false });
// xr3.Normalization is not accessible
xr3.GetType()
.GetProperty("Normalization", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(xr3, true);
var obj3 = (MyObject)ser.Deserialize(xr3);
Hacky, but still far preferable over implementing XmlTextReader from scratch which, given all the cleverness in the implementation, is not something to undertake lightly.
Note that XmlReader.Create is not contractually obligated to return an instance of a type that has a Normalization property, it just happens to do so in the current implementation.
I get a lof of syntax errors(missing simocolons...), when writing it like
IGlobal[] all;
public MainWindow()
{
InitializeComponent();
all = { new ATM(), new Bank()};
}
Even this does not work:
IGlobal[] all;
all= { new ATM(), new Bank()};
But as soon as I write it in one line(for example in a method) it works:
IGlobal[] all= { new ATM(), new Bank()};
"IGlobal" inherits only from "IDisposable". "ATM" and "Bank" inherit from "IGlobal" and 1 custom abstract class.
What is the problem here, what can I do ?
It is the syntax, where initializer and new is not exactly the same thing, and you cannot force it the way you want. All you can do is telling explicit array type
all = new IGlobal[] { new ATM(), new Bank() };
Or this will work too, but I suggest NOT to use it unless you want to deliberately make your co-workers confused.
all = new[] { (IGlobal) new ATM(), new Bank() };
I have a project similar(Almost identical) to Conference API project which is taking similar approach to the noted project for returning CollectionJson content. I am having difficulty Setting the Collection property of the ReadDocument (Line 30) as it does not have any setter. I could bypass this problem by doing the following change
public CollectionJsonContent(Collection collection)
{
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
collection.Version = "1.0";
Headers.ContentType = new MediaTypeHeaderValue("application/vnd.collection+json");
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
//var readDocument = new ReadDocument(); {IReadDocument.Collection = collection};
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer,collection);
writer.Flush();
}
_memoryStream.Position = 0;
}
Although above code compiles and to some extent sorts out the problem but then again I will have another problem of not being able to consume the JsonCollection content in my controller unit tests. Consider the following unit test code snippet:
using (var request = CreateRequest())
{
var controller = new TestController(DataService) {Request = request};
var temp = await controller.ListAsync(gridSearchData, sampleSearchData);
if ((temp is NotFoundResult) && (sampleCollection.Any()))
{
Assert.Fail("Controller did not return any result but query did");
}
var json = await temp.ExecuteAsync(cancellationTokenSource);
var readDocument = json.Content.ReadAsAsync<ReadDocument>(new[] {new CollectionJsonFormatter()}, cancellationTokenSource).Result;
}
Since I did not set the collection property of ReadDocument readDocument is always empty and I cant read its content.
How do you asynchronously read the contents of JsonCollection on the client side in WEB API projects?
To get a Clear picture of the approach look at the Conference Web Api
and the authors blog
OK all, this has been fixed. The Collection property is now settable again.
I have just pushed release 0.7.0 with this fix, a major naming refactoring as well as a nice improvement to serialization to not write out empty collections.
Please see the release notes for the changes (especially the naming as the package names and namespaces have changed)
As far as I see from your code, you do not serialize a ReadDocument object, but only a property of it (Collection), and then you try to deserialize that value into a new ReadDocument object.
A sample ReadDocument should serialize like this
"{"Collection": [1,2,3,4,5] }"
But you serialize collection, so you get
"[1,2,3,4,5]"
I recommend a surrogate class for serialization like this
class SerializableReadDocument
{
public Collection Collection { get; set; }
}
and update your serialization code like this
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
var readDocument = new SerializableReadDocument() { Collection = collection };
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, readDocument);
writer.Flush();
}
But, this will not resolve your problem when you try to deserialize your output since ReadDocument does not have a settable Collection property, deserialization will either fail, or return a ReadDocument object with an empty Collection.
You can use SerializableReadDocument if you like in your unit tests.
I am looking into this and will come up with a solution hopeful this weekend, which will either be to make it a public setter, or make the setter internal and have public ctor that accepts a collection.
Sorry for the difficulty.
I am serializing an object in code (not via a WCF call), and I am getting a little hung up with known types (I have used them with WCF, but not with the DataContract serializer as a "stand-alone" serializer)
I get an exception when I run the code below. I expected it to run without error because I supplied Known Types. What am I getting wrong here?
public class Parent {}
public class Child: Parent {}
// the code -- serialized is a serialized Child
// type is typeof(Parent) (I know it works if I pass typeof(Child), but isn't that what Known Types is all about??
// passing the known types seems to make no difference -- this only works if I pass typeof(Child) as the first param
var serializer = new DataContractSerializer(parentType, new Type[] {typeof(Child)});
object test = serializer.ReadObject(serialized);
Ok, so I'm having one of those days where I keep answering myself. The problme was not with the deserialization, it was in the serialization -- you must create the serializer with the same base type as the deserializer (I had created the serializer based upon the child type). For what it's worth, working code is below:
{
var child = new Child();
// here is where I went wrong before -- I used typeof(Child), with no known types to serialize
var serializer = new DataContractSerializer(typeof(Parent), new Type[] { typeof(Child) });
var stream = new MemoryStream();
serializer.WriteObject(stream, child);
stream.Position = 0;
serializer = new DataContractSerializer(typeof(Parent), new Type[] { typeof(Child) });
object test = serializer.ReadObject(stream);
stream.Dispose();
Console.WriteLine(test.ToString());
Console.ReadLine();
}