I have a class called MyBoard that looks like this
Public MyBoard
{
private BoardTile[,] tiles;
private bool tilesEliminated;
public int blabla {get; set; }
}
If i understand correctly the Serializable attribute serializes the entire class, what I'm attempting to do is to have the ability to serialize only the fields i want into XML format (other fields are irrelevant)
In my case i would like to write something that looks like this :
[ISerializable]
Public MyBoard
{
[SerializeMe]
private BoardTile[,] tiles;
private bool tilesEliminated;
public int blabla {get; set; }
}
I want to choose with some attribute what to serialize and what not, more over inside the class "BoardTile" i would like to do the same thing and choose the relevant fields also.
And then write something like this
main()
{
MyBoard m = new MyBoard();
//fill the tiles somehow.
m.SerializeToXml();
}
If i understand correctly i need to write a custom serializer by myself by inheriting ISerializable, but maybe it's possible to avoid it and letting .Net to serialize by himself automatically with some attributes that i don't know about.
By the way it's important to me that the output will be XML so i could read it.
I hope you understand my question, thanks for you help :)
Use DataContract attribute and XmlSerializer class:
[DataContract]
Public MyBoard
{
[DataMember]
pricate BoardTile[,] tiles;
private bool tilesEliminated;
public int blabla {get; set; }
}
And serialization:
var board = new MyBoard();
var ser = new DataContractSerializer(typeof(MyBoard));
var sw = new StringWriter();
ser.WriteObject(sw, board);
sw.Close();
var xml = sw.ToString();
It will serialize only members that are marked with [DataMember] attribute and skip the other ones.
Related
I need to generate an XML document that follows this specifictaion
<productName locale="en_GB">Name</productName>
but using XMLSeralization I am getting the following
<productName locale="en_GB">
<Name>Name</Name>
</productName>
My C# code is like this:
[Serializable]
public class productName
{
public productName()
{
}
public string Name;
[XmlAttribute]
public string locale;
}
XmlAttribute is what is required to show the locale in the correct place, but I am unable to figure out how to export the Name field correctly.
Does anyone have an idea?
Thanks
EDIT:
This is the code to generate the XML
public static class XMLSerialize
{
public static void SerializeToXml<T>(string file, T value)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = XmlWriter.Create(file))
serializer.Serialize(writer, value);
}
public static T DeserializeFromXML<T>(string file)
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(file);
T result;
result = (T)deserializer.Deserialize(textReader);
textReader.Close();
return result;
}
}
Instead of specifying Name as element specify it as text value by adding [XmlText] attribute
[XmlText]
public string Value { get; set; }
This contains not only a direct answer to your question, but more of a indirect answer of how to solve similar issues like this in the future.
Start the other way around, with your xml, write your xml exactly like you want it and go from there, like this:
// assuming data.xml contains the xml as you'd like it
> xsd.exe data.xml // will generate data.xsd, ie xsd-descriptor
> xsd.exe data.xsd /classes // will generate data.cs, ie c# classes
> notepad.exe data.cs // have a look at data.cs with your favorite editor
Now just have a look at data.cs, this will contain an enormous amount of attributes and stuff and the namespaces are probably wrong, but at least you know how to solve your particular xml-issue.
The direct answer is to use the XmlTextAttribute on the given property, preferably named Value since that is the convention I've seen so far.
[Serializable]
public class productName {
public productName() { }
[XmlText]
public string Value {get; set;}
[XmlAttribute]
public string locale {get; set;}
}
I hope to find a solution from you. What I need is to serialize ValidatorList class object into an xml document. How to do this?
I tried like this:
XmlSerializer _serializer = new XmlSerializer(list);
But I don't know how to make output of xml for list that has several classes.
C#
_list= new ListVal();
Type _type = typeof(ras);
_list.Add(new RequiredField
{
Property = _type.GetProperty("CustRef")
}.Add(new AsciiVal()));
_list.Add(new RequiredField
{
Property = _type.GetProperty("ctr")
}.Add(new StringLengthVal
{
min= 3,
max= 3
}));
[Serializable]
public class Field
{
public Field Next
{
get;
set;
}
public Field TypeName
{
get;
set;
}
public Field PropertyName
{
get;
set;
}
}
public class RequiredField : Field
{
//TODO
}
public class AsciiVal: Field
{
//TODO
}
public class StringLengthVal: Field
{
//TODO
}
public class ListVal: List<Field>
{
//TODO
}
I have something close, but not exactly the Xml you want. In actual fact I think you'll see that the Xml produced below makes a bit more sense than what you have.
To get you started, you control the serialization and deserialization using attributes in the System.Xml.Serialization namespace. A few useful ones to read up on are
XmlRootAttribute
XmlElementAttribute
XmlAttributeAttribute
XmlIncludeAttribute
So I mocked up some code which closely matches your own. Notice the addition of some attributes to instruct the serializer how I want the Xml to be laid out.
[XmlInclude(typeof(AsciiValidator))]
[XmlInclude(typeof(RequiredValidator))]
[XmlInclude(typeof(StringLengthValidator))]
public class FieldValidator
{
[XmlElement("Next")]
public FieldValidator Next
{
get;
set;
}
[XmlElement("PropertyName")]
public string PropertyName
{
get;
set;
}
}
public class AsciiValidator: FieldValidator
{
}
public class RequiredValidator: FieldValidator
{
}
public class StringLengthValidator: FieldValidator
{
[XmlElement]
public int MinLength{get;set;}
[XmlElement]
public int MaxLength{get;set;}
}
[XmlRoot("ValidatorList")]
public class ValidatorList : List<FieldValidator>
{
}
Point of interest; Every class inheriting FieldValidator must be added to the list of known types using XmlIncludeAttribute so the serializer knows what to do with them)
Then I created an example object map:
var test = new ValidatorList();
test.Add(
new RequiredValidator()
{
PropertyName="CustRef",
Next = new AsciiValidator()
});
test.Add(
new RequiredValidator()
{
PropertyName="CurrencyIndicator",
Next = new StringLengthValidator(){
MinLength=3,
MaxLength = 10
}
});
Finally I told the serializer to serialize it (and output the result to the console)
var ser = new XmlSerializer(typeof(ValidatorList));
ser.Serialize(Console.Out,test);
This was the result:
<?xml version="1.0" encoding="utf-8"?>
<ValidatorList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="AsciiValidator" />
<PropertyName>CustRef</PropertyName>
</FieldValidator>
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="StringLengthValidator">
<MinLength>3</MinLength>
<MaxLength>10</MaxLength>
</Next>
<PropertyName>CurrencyIndicator</PropertyName>
</FieldValidator>
</ValidatorList>
Not a million miles away from what you wanted. There is the need to output certain things in a certain way (eg xsi:type tells the serializer how to deserialize back to the object map). I hope this gives you a good start.
Here is a live, working example: http://rextester.com/OXPOB95358
Deserialization can be done by calling the Deserialize method on the XmlSerializer.
For example, if your xml is in a string:
var ser = new XmlSerializer(typeof(ValidatorList));
var test = "<..../>" // Your Xml
var xmlReader = XmlReader.Create(new StringReader(test));
var validatorList = (ValidatorList)ser.Deserialize(xmlReader);
There are many overrides of Deserialize which take differing inputs depending if the data is in a Stream an existing reader, or saved to a file.
You have to decorate the class that contains the _validators field with the KonwnType attribute
[Serializable]
[KwownType(typeof(RequiredFieldValidator)]
[KwownType(typeof(AsciValidator)]
public class MySerialisableClass
I have several SO answers that detail how to serialize objects using XML. I'll provide links below.
However, since you're looking for a rather simple serialization of your object, you may want to read up on the DataContractSerializer. It's much less complicated than the old .NET 1.x XML Serialization.
Here's the list of SO answers:
Omitting all xsi and xsd namespaces when serializing an object in .NET?
XmlSerializer: remove unnecessary xsi and xsd namespaces
Suppress xsi:nil but still show Empty Element when Serializing in .Net
Using XmlAttributeOverrides on Nested Properties
Even though many of these have to do with XML serialization and namespaces, they contain complete examples of serializing an object to XML using .NET 1.x XML Serialization.
In my class (Camera), that I need to serialize, I have one member which doesn't have much serialization means and I can't modify its class. But there are only 2-3 parameters out of whole class that I can serialize and then de-serialize based on that.
XmlSerializer SerializerObj = new XmlSerializer(typeof(Camera));
TextWriter WriteFileStream = new StreamWriter(#"C:\test.xml");
SerializerObj.Serialize(WriteFileStream, cc);
WriteFileStream.Close();
How and where can I put custom serialization code, to take those 2-3 parameters from object and give to serializer, then during de-serialization create again that object using those 2-3 parameters.
Update. The example code:
[Serializable()]
public class Camera
{
public string name;
public int index;
public double distance;
public List<string> CameraList { get; set; }
[XmlIgnore()]
public GMarkerGoogle marker;
public Camera()
{
}
}
marker is the member that doesn't have serialization means. But there with marker.position, marker.rotation I can re-create that object.
If you can't or don't want to Xml serialize your GMarker then make sure that you do serialize the properties needed to reconstruct it (kind of what you already suggested). That way you can make a property with backing field to lazy recreate the GMarker.
[Serializable()]
public class Camera
{
public string name;
public int index;
public double distance;
public List<string> CameraList { get; set; }
private GMarkerGoogle _marker;
[XmlIgnore()]
public GMarkerGoogle Marker
{
set
{
_marker = value;
MarkerPosition = _marker.position;
MarkerRotation = _marker.rotation;
}
get
{
if (_marker == null)
{
_marker = new GMarkerGoogle(MarkerPosition, MarkerRotation);
}
return _marker;
}
}
public double MarkerPosition { get; set; }
public double MarkerRotation { get; set; }
public Camera()
{
}
}
Does this help? I made up a thing or two because I don't know the API for the marker, but I hope you can draw the picture from this point onwards.
By implementing the IXmlSerializable interface in your Camera class.
Check MSDN for more info.
Alternatively you could use the [XmlIgnore] attribute to flag the properties you do not want to serialize to XML.
Check MSDN for more info :).
PS: If you post some code I can show you an example but you probably don't need me. Let me know if you do though.
I'm attempting to write a set of classes to represent a particularly complex object, and in one of those classes, I have a property that is set as the base (abstract) class of three possible derived classes. I'm setting up an ASP.NET Web API to handle the serialization and deserialization, which means that, by default, it uses Json.NET for JSON. How can I get the Web API to properly deserialize JSON sent via POST or PUT into the proper derived class?
The class with the abstract member looks like this (I'm including the Xml decorators for clarity and because they work perfectly well for deserializing xml using the XmlSerializer)
[Serializable]
public class FormulaStructure {
[XmlElement("column", typeof(ColumnStructure))]
[XmlElement("function", typeof(FunctionStructure))]
[XmlElement("operand", typeof(OperandStructure))]
public AFormulaItemStructure FormulaItem;
}
The abstract class is pretty basic:
[Serializable]
public abstract class AFormulaItemStructure { }
And there are three derivatives of the abstract class:
[Serializable]
public class ColumnStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("field")]
public string Field;
[XmlAttribute("display")]
public string Display;
}
[Serializable]
public class FunctionStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlAttribute("name")]
public string Name;
[XmlElement("parameters")]
public string Parameters;
}
[Serializable]
public class OperandStructure: AFormulaItemStructure {
[XmlAttribute("type")]
public string Type;
[XmlElement("left")]
public string Left;
[XmlElement("right")]
public string Right;
}
At present, using [DataContract] attributes, the Json.NET formatter fails to populate the derived class, leaving the property null.
Questions
Can I mix XmlSerializer attributes with DataContractSerializer attributes on the same class? I use the XmlSerializer because I use xml attributes in the xml I designed, but that can be changed if necessary since I am developing the xml schema myself.
What is the equivalent in Json.NET to [KnownType()] ? Json.NET doesn't appear to respect the DataContractSerializer version of KnownType. Will I need to roll my own JsonConverter to determine the proper type?
How would I decorate the classes so that DataContractSerializer or DataContractJsonSerializer will properly deserialize the objects in both Xml and Json? My goal is to put this into an ASP.NET Web API, so I want the flexibility to generate Xml or Json, as appropriate to the requested type. Is there an alternative formatter that I need to use to work with this complex class, if Json.NET won't work?
I need the ability to generate an object on the client side without necessarily including the .NET class names into the object.
Testing and Refinement
In my testing of the Web API, the default serialization sends down to the client:
{"FormulaItem":{"type":"int","field":"my_field","display":"My Field"}}
which is ideal for my purposes. Getting this to go back to the API and deserialize into the proper derived types, though, isn't working (it's generating null for the property).
Testing Tommy Grovnes answer below, the DataContractSerializer he used for testing generates:
{"FormulaItem":{"__type":"column:#ExpressionStructureExperimentation.Models","display":"My Field","field":"my_field","type":"int"}}
which doesn't work for me, or for code maintainability (refactoring becomes a PITA if I hard-code the entire namespace into the JavaScript for generating these objects).
You can mix as mentioned already but I don't think you need to, haven't used WEB api myself but WCF Rest produces xml and json from DataContracts (without Xml.. tags), tag your classes like this:
[DataContract]
public class FormulaStructure
{
[DataMember]
public AFormulaItemStructure FormulaItem;
}
[DataContract]
[KnownType(typeof(ColumnStructure))]
[KnownType(typeof(FunctionStructure))]
[KnownType(typeof(OperandStructure))]
public abstract class AFormulaItemStructure { }
[DataContract(Name="column")]
public class ColumnStructure : AFormulaItemStructure
{
[DataMember(Name="type")]
public string Type;
[DataMember(Name = "field")]
public string Field;
[DataMember(Name = "display")]
public string Display;
}
[DataContract(Name="function")]
public class FunctionStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "name")]
public string Name;
[DataMember(Name = "parameters")]
public string Parameters;
}
[DataContract(Name = "operand")]
public class OperandStructure : AFormulaItemStructure
{
[DataMember(Name = "type")]
public string Type;
[DataMember(Name = "left")]
public string Left;
[DataMember(Name = "right")]
public string Right;
}
If you need more control over the XML/JSON generated you might have to tweak this further. I used this code to test:
public static string Serialize(FormulaStructure structure)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
var serializer = new DataContractSerializer(typeof(FormulaStructure));
serializer.WriteObject(memoryStream, structure);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
public static FormulaStructure Deserialize(string xml)
{
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var deserializer = new DataContractSerializer(typeof(FormulaStructure));
return (FormulaStructure)deserializer.ReadObject(stream);
}
}
After we ran into some issues much further down the line with my previous answer, I discovered the SerializationBinder class that JSON can use for serializing/deserializing namespaces.
Code First
I generated a class to inherit the SerializationBinder:
public class KnownTypesBinder : System.Runtime.Serialization.SerializationBinder {
public KnownTypesBinder() {
KnownTypes = new List<Type>();
AliasedTypes = new Dictionary<string, Type>();
}
public IList<Type> KnownTypes { get; set; }
public IDictionary<string, Type> AliasedTypes { get; set; }
public override Type BindToType(string assemblyName, string typeName) {
if (AliasedTypes.ContainsKey(typeName)) { return AliasedTypes[typeName]; }
var type = KnownTypes.SingleOrDefault(t => t.Name == typeName);
if (type == null) {
type = Type.GetType(Assembly.CreateQualifiedName(assemblyName, typeName));
if (type == null) {
throw new InvalidCastException("Unknown type encountered while deserializing JSON. This can happen if class names have changed but the database or the JavaScript references the old class name.");
}
}
return type;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName) {
assemblyName = null;
typeName = serializedType.Name;
}
}
How it works
Let's say I have a set of classes defined thus:
public class Class1 {
public string Text { get; set; }
}
public class Class2 {
public int Value { get; set; }
}
public class MyClass {
public Class1 Text { get; set; }
public Class2 Value { get; set; }
}
Aliased Types
What this does is allows me to generate my own names for classes that will be serialized/deserialized. In my global.asax file, I apply the binder as such:
KnownTypesBinder binder = new KnownTypesBinder()
binder.AliasedTypes["Class1"] = typeof(Project1.Class1);
binder.AliasedTypes["WhateverStringIWant"] = typeof(Project1.Class2);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.Binder = binder;
Now, whenever I serialize, say, MyClass as JSON, I get the following:
{
item: {
$type: "Project1.MyClass",
Text: {
$type: "Class1",
Text: "some value"
},
Value: {
$type: "WhateverStringIWant",
Value: 88
}
}
}
Known Types
I can also choose to strip off the assembly information and strictly use the class name by adding information to the KnownTypesBinder:
KnownTypesBinder binder = new KnownTypesBinder()
binder.KnownTypes.Add(typeof(Project1.Class1));
binder.KnownTypes.Add(typeof(Project1.Class1));
In the two examples given, Class1 is referenced the same way. However, if I refactor Class1 to, say, NewClass1, then this second example will start sending a different name. That may or may not be a big deal, depending on whether you are using the types or not.
Final Thoughts
The advantage of the AliasedTypes is that I can give it any string that I want, and it doesn't matter how much I refactor the code, the communication between the .NET and the JavaScript (or whatever consumer is out there) is unbroken.
Be careful not to mix AliasedTypes and KnownTypes that have the exact same class name, because the code is written that the AliasType will win out over KnownType. When the binder doesn't recognize a type (aliased or known), it will provide the full assembly name of the type.
In the end, I broke down and added the .NET class information to the module in string variables to make refactoring easier.
module.net = {};
module.net.classes = {};
module.net.classes['column'] = "ColumnStructure";
module.net.classes['function'] = "FunctionStructure";
module.net.classes['operand'] = "OperandStructure";
module.net.getAssembly = function (className) {
return "MyNamespace.Models." + module.net.classes[className] + ", MyAssembly";
}
and generated the JSON as
{
"FormulaItem": {
"$type": module.net.getAssembly('column'),
"type": "int",
"field": "my_field",
"display": "My Field"
}
}
I'm trying to serialize a class that derives from BindingList(Floor), where Floor is a simple class that only contains a property Floor.Height
Here's a simplified version of my class
[Serializable]
[XmlRoot(ElementName = "CustomBindingList")]
public class CustomBindingList:BindingList<Floor>
{
[XmlAttribute("publicField")]
public string publicField;
private string privateField;
[XmlAttribute("PublicProperty")]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
}
I'll serialize an instance of CustomBindingList using the following code.
XmlSerializer ser = new XmlSerializer(typeof(CustomBindingList));
StringWriter sw = new StringWriter();
CustomBindingList cLIst = new CustomBindingList();
Floor fl;
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
ser.Serialize(sw, cLIst);
string testString = sw.ToString();
Yet testString above ends getting set to the following XML:
<CustomBindingList xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Floor Height="10" />
<Floor Height="10" />
<Floor Height="10" />
</CustomBindingList>"
How do I get "publicField" or "publicProperty to serialize as well?
The short answer here is that .NET generally expects something to be a collection xor to have properties. This manifests in a couple of places:
xml serialization; properties of collections aren't serialized
data-binding; you can't data-bind to properties on collections, as it implicitly takes you to the first item instead
In the case of xml serialization, it makes sense if you consider that it might just be a SomeType[] at the client... where would the extra data go?
The common solution is to encapsulate a collection - i.e. rather than
public class MyType : List<MyItemType> // or BindingList<...>
{
public string Name {get;set;}
}
public class MyType
{
public string Name {get;set;}
public List<MyItemType> Items {get;set;} // or BindingList<...>
}
Normally I wouldn't have a set on a collection property, but XmlSerializer demands it...
XML serialization handles collections in a specific way, and never serializes the fields or properties of the collection, only the items.
You could either :
implement IXmlSerializable to generate and parse the XML yourself (but it's a lot of work)
wrap your BindingList in another class, in which you declare your custom fields (as suggested by speps)
This is known issue with XML serialization and inheriting from collections.
You can read more info on this here : http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/0d94c4f8-767a-4d0f-8c95-f4797cd0ab8e
You could try something like this :
[Serializable]
[XmlRoot]
public class CustomBindingList
{
[XmlAttribute]
public string publicField;
private string privateField;
[XmlAttribute]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
[XmlElement]
public BindingList<Floor> Floors = new BindingList<Floor>();
}
This means you can add floors by using Floors.Add and you will get the result you want, I hope, however, I didn't try it. Keep in mind that playing around with attributes is the key to XML serialization.