I am trying to create an xml document with the information that is taken from a test. Basically I am using {get; set:} to get the information and when I walk through the program you see where the file information is passed through but I either get the error unable to create the xml document or it is created but is blank.
Here is the code:
public class XmlCreate
{
public string Types { set; get; }
public DateTime Time { get; set; }
public bool done{ get; set; }
public void SerializetoXml(IRepo repo)
{
var filename = string.Format("{0}__{1}", DateTime.Now.ToString("yyyyMMdd"), "Log.xml");
var path =
#"C:\TestDocs\artifacts";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
var fullpath = Path.Combine(filename, path);
var serializer = new XmlSerializer((typeof(IRepo)));
var textwriter = new StreamWriter(filename);
serializer.Serialize(textwriter, repo);
textwriter.Close();
}
}
The XML serializer needs to know about all the object types it will encounter ahead of time. If you're still trying to create the serializer using an interface instead of a class type then that might be part of the problem; an interface won't necessarily expose all of the public properties of your object. Otherwise you need to make sure that any types which the XML serializer can't automatically infer from your properties (e.g. due to polymorphism) are included. Use the XmlInclude attribute on your root object to inform the serializer about these types. Example:
// XmlInclude is necessary because our class doesn't explicitly mention derived object
[XmlInclude(typeof(ObjectDerived))]
class MyRootClass {
public ObjectBase { get; set; }
}
class ObjectBase {
// some properties here
}
class ObjectDerived : ObjectBase {
// more properties here
}
...
var serializer = new XmlSerializer(typeof(MyRootClass));
Use full path as
Path.Combine(path, filename)
not the other way round.
your path name turned out to be wrong
EDIT:
You cant serialize by passing an Interface object.
You can modify your code as provided in this link
or this
Related
I am using .net Core 1.1 and writing a console application. I need to Serialize an Object with some basic properties of strings and int as well as some IEnumerable Objects and serialize it as XML so I can save it to a "data.xml" file.
I receive the Exception:
System.Runtime.Serialization.SerializationException: 'Type 'System.Collections.Generic.List`1[[CorpsDataExtractor.ViewModels.LegacyView, CorpsDataExtractor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' with data contract name 'ArrayOfLegacyView:http://schemas.datacontract.org/2004/07/CorpsDataExtractor.ViewModels' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'
I currently am able to serialize my Object as JSON, but am having trouble utilizing the DataContractSerializer. I Added the Known Type List that the exception states I am missing but it still fails.
// This List isretrieved using Entity Framework Core
List<LegacyView> legacyData = await _corpsRepo.ListLegacyData();
// The Below Line will write to a JSON File. No Error and works
File.WriteAllText(#"E:\\CorporationsDataLegacy.json", JsonConvert.SerializeObject(legacyData));
// This Creates a Known Type List for the DataContractSerializer
var knownTypeList = new List<Type>
{
typeof(LegacyView),
typeof(People),
typeof(DocumentType)
};
FileStream writer = new FileStream(#"E:\\data.xml", FileMode.Create);
var ser = new DataContractSerializer(typeof(LegacyView), knownTypeList );
// When Debugging I Receive The stated exception at this line
ser.WriteObject(writer, legacyData);
Below Are my Class ViewModels:
Legacy View
[DataContract]
public class LegacyView
{
[DataMember]
public string Ubi { get; set; }
[DataMember]
public IEnumerable<Person> People{ get; set; }
[DataMember]
public IEnumerable<DocumentType> DocumentTypes { get; set; }
}
Person
[DataContract]
public class Person
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string MiddleName { get; set; }
}
Documents
[DataContract]
public class DocumentType
{
[DataMember]
public string Document { get; set; }
[DataMember]
public DateTime? CompletedDate { get; set; }
}
Any Direction on what I am missing?
I might be a bit off as I've done more of XmlSerialize rather than DataContracts one, but I believe both assume eventual deserialization of the content. And with IEnumerable<T> as the type, deserializer wouldn't know what class to actually instantiate to back the interface during deserialization. Change fields to concrete classes instead of enumeration, and that should work.
By using a Both Gusman and LB2 comments I was able to solve the issue. As LB2 states "with IEnumerable as the type, deserializer wouldn't know what class to actually instantiate to back the interface during deserialization."
This is why the error was occurring, however the problem did not exist at the Class LegacyView [DataMember] level, it existed at the known Types that were being passed into the DataContractSerializer.
Gusman was correct that I needed to tell the DataContractSerializer that these were going to be an enumerable type.
// I needed to tell the DataContractSerializer what the types were
var knownTypeList = new List<Type>
{
typeof(List<LegacyView>),
typeof(List<People>),
typeof(List<DocumentType>)
};
using (var writer = new FileStream(#"E:\\myTestExample.xml", FileMode.Create))
{
var ser = new DataContractSerializer(typeof(LegacyView), knownTypeList);
ser.WriteObject(writer, legacyData);
}
Say I have the following classes:
[DataContract]
class Entry<T>
{
[DataMember]
public T Data { get; set; }
}
[DataContract]
class DataList<T>
{
[DataMember]
public IList<T> Collection { get; set; }
}
[DataContract]
[KnownType(typeof(User))]
class ExtendedDataList : DataList<object>
{
[DataMember]
public string SomeExtraParameter { get; set; }
}
[DataContract]
class User
{
[DataMember]
public string Name { get; set; }
}
I've applied KnownTypeAttribute to ExtendedDataList since that class extends the base class of general type object, and it will store different kinds of objects in the list. In this example, I've marked a known type of User since I know it'll contain User objects.
Here's the serialization code:
var user = new User { Name = "Bob" };
var users = new ExtendedDataList { Collection = new List<object> { user } };
serialize(users);
with
static void serialize<T>(T obj)
{
var entry = new Entry<T>();
entry.Data = obj;
var stream = new MemoryStream();
var serializer = new DataContractJsonSerializer(typeof(Entry<T>));
serializer.WriteObject(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
var r = new StreamReader(stream);
var s = r.ReadToEnd();
System.Diagnostics.Debug.WriteLine(s);
}
the line serializer.WriteObject(stream, entry); throws a SerializationException saying that the type User was not expected and that I should use KnownTypeAttribute to specify it. But I did (indirectly)!
How can I make this work? I cannot move KnownType to the Entry<T> class, because it needs to be general. Why can't DataContractJsonSerializer see that ExtendedDataList specifies the User as a known type?
I figured out a way to get it to work, but it still doesn't explain why DataContractJsonSerializer is ignoring the KnownType attribute I've applied to ExtendedDataList.
It seems that DataContractJsonSerializer isn't smart enough to notice the KnownType attributes that I've specified on the ExtendedDataList class; apparently it only honors the known types that I've attached to the Entry<T> class since typeof(Entry<T>) is the type that I give to the constructor of DataContractJsonSerializer
I want Entry<T> to remain general, and I do not want to litter it with all sorts of KnownType attributes. So instead, I use the following class definition:
[DataContract]
[KnownType("KnownTypes")]
class Entry<T>
{
[DataMember]
public T Data { get; set; }
public static IEnumerable<Type> KnownTypes()
{
var attrs = typeof(T).GetTypeInfo().GetCustomAttributes<KnownTypeAttribute>();
return attrs.Select(attr => attr.Type);
}
}
This uses reflection to get the KnownType attributes attached to the inner type T and then passes these types to whatever data contract serializer that serializes Entry<T>.
Note: requires using System.Linq and using System.Reflection.
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.
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 have two classes SccmAction and TicketAction which both implement interface IDelivery. These classes will be transmitted on a processing queue from which I just want to pull the message and act upon the Deliver method.
It seems however that I cannot deserialize to an interface because when I attempt to do so a System.NotSupportedException is thrown. From what I have gathered the XMLSerializer requires a concrete type for serialization. Does this leave me having to create an abstract class which implements IDelivery and inheriting SccmAction and ArsAction from it? Have I missed something that?
Edit Further Clarification
I may have a design flaw but my purpose is to pull messages off of a queue containing these objects. My intention was to have all objects coming off of this queue implement the interface ensuring that the application processing the objects just operates on the Deliver method. Each objects implementation of Deliver would vary.
Code to Deserialize
using (SqlConnection connection =
new SqlConnection(ConnectionString))
{
SqlCommand command = new SqlCommand(ReceiveString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
byte[] sb = (byte[])reader["message_body"];
SccmAction sa = DeserializeObject<SccmAction>(sb);
IDelivery iD = DeserializeObject<IDelivery>(sb);
}
reader.Close();
}
public static T DeserializeObject<T>(byte[] ba)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream(ba);
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
Classes and Interface
[Serializable]
public class SccmAction : IDelivery
{
public string MachineName { get; set; }
public string CollectionName { get; set; }
public string Action { get; set; }
public DateTime EntryDateTime { get; set; }
public SccmAction () { }
public void Deliver()
{
throw new NotImplementedException();
}
}
[Serializable]
public class ArsAction : IDelivery
{
public string MachineName { get; set; }
public string CustomerName { get; set; }
public ArsAction() { }
public void Deliver()
{
throw new NotImplementedException();
}
}
public interface IDelivery
{
void Deliver();
}
Simply put, you can't serialize an interface without any dirtiness. But why do you want to serialize your interface anyway, it doesn't hold any state. It only has a declaration for a method.
You might want to create a Serialize method inside your classes. So they can serialize themselves individually, they know which type they are.
Besides, how do you expect the XmlSerializer to Deserialize to an exact type without providing it? It can't simply pick what to Deserialize to....
You could however change this line
IDelivery iD = DeserializeObject<IDelivery>(sb);
to this
IDelivery iD = DeserializeObject<SccmAction>(sb);
If you dont know what type you are deserializing to initially (ArsAction or SccmAction), then you can do something like this.
public static IDelivery DeserializeFromString(string xml)
{
//replace this stream with whatever you are useing
StringReader strReader = new StringReader(xml);
XmlReader reader = new XmlTextReader(fs); //important to use XmlReader
reader.MoveToContent(); //move to root
String className = reader.Name.Trim(); //read the class name
//use the namespace IDelivery is located in
className = "IDeliveryNamespace." + className;
//get the type
Type classType = Type.GetType(className);
XmlSerializer serializer = new XmlSerializer(Type.GetType(className));
// Declare an object variable of the type to be deserialized.
IDelivery i;
// Use the Deserialize method to restore the object's state.
i = (IDelivery)Convert.ChangeType(serializer.Deserialize(reader),classType);
return i;
}
Of course this assumes that you your Serializable class Name is not changed. Meaning ArsAction class serializes to and as far as I can tell from what you posted that is the case.
This code is a little dirty, but it should give you a starting point.