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.
Related
I get a file with different content (currently 4 different classes), either
<ClassA><!-- content --></ClassA>
or
<ClassB><!-- content --></ClassB>
or ...
At time of parsing I have no further information which class is in the file.
So, currently, I try to parse by trial and error:
try
{
ClassA result = (ClassA)new XmlSerializer(typeof(ClassA)).Deserialize(reader);
if(!(result is null)) { \\do something }
}
catch (Exception) {}
And the same for ClassB and so on ...
Is there a more elegant way to parse the classes?
I can give all classes the same base class, although the are quite different in their form.
I solved this issue with XmlSerializer.CanDeserialize(XmlReader)
using (var reader = XmlReader.Create(stream))
{
foreach (var type in types)
{
var serializer = new XmlSerializer(type);
if (serializer.CanDeserialize(reader))
{
return serializer.Deserialize(reader);
}
}
throw new XmlException("Invalid xml type");
}
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 making a WPF application in which I save list of object on exit of my WPF Application. And get the list of objects on system startup. Everything works fine initially. But Some times it gives the serialization Exception. After getting the exception I looked of the xml serialized file. But it seem to me that the exception was thrown because the xml file was not formed properly. When I corrected it. It again worked fine.
public static class IsolatedStorageCacheManager<T>
{
public static void store(T loc)
{
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
using(IsolatedStorageFileStream fileStream=appStore.OpenFile("myFile21.xml",FileMode.OpenOrCreate))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(fileStream, loc);
}
}
public static T retrieve()
{
T obj = default(T);
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
if (appStore.FileExists("myFile21.xml"))
{
using (IsolatedStorageFileStream fileStream = appStore.OpenFile("myFile21.xml", FileMode.Open))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
try
{
obj = (T)serializer.ReadObject(fileStream);
}
catch (SerializationException e)
{
Console.WriteLine(e.StackTrace);
}
}
}
return obj;
}
}
The first thing to do is make sure that the objects passed to store are of a type supported by the DataContractSerializer.
The easiest thing to do is check all store calls yourself.
You could also create a validation method or even better, see if anyone else has implemented one. This method could validate the loc object and return a boolean and be called at the beginning of the store method inside a System.Diagnostics.Debug.Assert call so that it will only run on a debug configuration. Note though that this method could be quite tricky because you will have to validate type T for all the cases mentioned in the specification of DataContractSerializer and if T is a generic validate T's parameter(s) as well.
I am using xml-serialization in my project to serialize and deserialize objects based on an xml schema. I used the xsd tool to create classes to use when serializing / deserializing the objects.
When I go to serialize the object before sending, I am forced to set the *Specified property to true in order to force the serializer to serialize all propeties that are not of type string.
Is there a way to force the serialization of all properties without having to set the *Specified property to true?
The FooSpecified property is used to control whether the Foo property must be serialized. If you always want to serialize the property, just remove the FooSpecified property.
I know this is an old question, but none of the other answers (except perhaps the suggestion of using Xsd2Code) really produces an ideal solution when you're generating code as part of your build and your .xsd may change several times during a single release cycle.
An easy way for me to get what I really wanted and still use xsd.exe was to run the generated file through a simple post-processor. The code for the post-processor is as follows:
namespace XsdAutoSpecify
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
class Program
{
static void Main(string[] args)
{
try
{
if (args.Length != 1)
{
throw new ArgumentException("Specify a file name");
}
string fileName = args[0];
Regex regex = new Regex(".*private bool (?<fieldName>.*)Specified;");
IList<string> result = new List<string>();
IDictionary<string, string> edits = new Dictionary<string, string>();
foreach (string line in File.ReadLines(fileName))
{
result.Add(line);
if (line.Contains("public partial class"))
{
// Don't pollute other classes which may contain like-named fields
edits.Clear();
}
else if (regex.IsMatch(line))
{
// We found a "private bool fooSpecified;" line. Add
// an entry to our edit dictionary.
string fieldName = regex.Match(line).Groups["fieldName"].Value;
string lineToAppend = string.Format("this.{0} = value;", fieldName);
string newLine = string.Format(" this.{0}Specified = true;", fieldName);
edits[lineToAppend] = newLine;
}
else if (edits.ContainsKey(line.Trim()))
{
// Use our edit dictionary to add an autospecifier to the foo setter, as follows:
// set {
// this.fooField = value;
// this.fooFieldSpecified = true;
// }
result.Add(edits[line.Trim()]);
}
}
// Overwrite the result
File.WriteAllLines(fileName, result);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Environment.Exit(-1);
}
}
}
}
The result is generated code similar to the following:
[System.Xml.Serialization.XmlAttributeAttribute()]
public barEnum foo {
get {
return this.fooField;
}
set {
this.fooField = value;
this.fooFieldSpecified = true;
}
}
You could add a default value to your schema and then use the DefaultValueAttribute.
For example, you could have the following in your schema:
<xs:element name="color" type="xs:string" default="red"/>
And then the following property for serialization:
[DefaultValue(red)]
public string color { get; set; }
This should force the color property to always serialize as "red" if it has not been explicitly set to something else.
I faced same issue and ended up setting all *Specified properties to true by reflection.
Like
var customer = new Customer();
foreach (var propertyInfo in typeof(Customer).GetProperties().Where(p => p.Name.EndsWith("Specified")))
{
propertyInfo.SetValue(customer, true);
}
We found that the answer to this question is to make sure that the schema elements are all defined as string data types. This will make sure that the serializer serializes all fields without the use of the correlated *specified property.
I built my *.dbml file with the required tables and that generated the relationships, 1 to many.
One of my methods in my WCF Service library has this query
IQueryable<Client>localClient = from c in db.Clients
where c.ClientID.Equals(2612)
select c;
foreach(Client currentClient in localClient)
{
//Call serialize method here
}
One of the table which client has a 1 to many relationships is Client - Employee
With 'localClient', I want to serialize that and return to the invoker. However it tells me that the the XML document could not be formed.
This is the InnerException:
A circular reference was detected while serializing an object of type TestDB_Public.Employee.
My serialization code
public string Serialize(object o, XmlSerializerNamespaces ns)
{
try
{
System.IO.MemoryStream m = new System.IO.MemoryStream();
if (ns != null)
serializer.Serialize(m, o, ns);
else
serializer.Serialize(m, o);
m.Position = 0;
byte[] b = new byte[m.Length];
m.Read(b, 0, b.Length);
return System.Text.UTF8Encoding.UTF8.GetString(b);
}
catch (Exception ex)
{
return "Ex = " + ex.ToString();
}
}
Is serialization of IQueryable<> with 1 to many relationships not possible?
What exactly is the error message? Note that you have to serialize something concrete like a list or array of objects (not a query).
If you want queryable over the wire, look at ADO.NET Data Services, which does this.
Also - have you set the serialization mode to "unidirectional" in the dbml designer? As long as there are no loops, it should work fine.
You can't serialise an object graph that has cyclical relationships:
class Employee
{
Employee Manager;
List<Employee> Employees;
}
var bossMan = new Employee();
var emp2 = new Employee{Manager = bossMan}
var bossMan.Employees.Add(emp2);
If you now try to serialise bossman or emp2, you will get the exception.
Have a look at this post, check the Cyclic Object Graphs for a solution.
Marc:
For some reason it is not allowing me to add a comment;
I added this
[Table(Name="dbo.Client")]
[DataContract(IsReference=true)]
public partial class Client: INotifyPropertyChanging, INotifyPropertyChanged
{
..//
private EntitySet<ClEmp> _ClEmp;
[Association(N...)]
[DataMember(Order=70, EmitDefaultValue=false)]
public EntitySet<ClEmp> ClEmps
}
My serialization is this:
DataContractSerializer ser =
new DataContractSerializer(typeof(Client));
var ms = new System.IO.MemoryStream();
ser.WriteObject(ms, r);
ms.Seek(0, System.IO.SeekOrigin.Begin);
var sr = new System.IO.StreamReader(ms);
var xml = sr.ReadToEnd();
when i look at var xml, i do not get my enity set ClEmp.