I'm using the XmlSerializer class from System.Xml.Serialization
My datatypes are marked with the appropriate attributes
e.g.
[XmlRoot(ElementName = "XMLFile")]
public class OrderXml
{
[XmlArray(ElementName = "SalesOrders")]
[XmlArrayItem(ElementName = "SalesOrder")]
public List<SalesOrder> SalesOrders { get; set; }
public OrderXml()
{
}
}
When serializing, there are no problems.
using (FileStream fs = new FileStream(save_to_path + $"Orders_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.xml", FileMode.Create))
{
xs.Serialize(fs, new OrderXml() { SalesOrders = order_resp.results });
}
However, when attempting to read the files that this program has written,
there are no exceptions but all of the fields have default values:
OrderXml ox = null;
using(Stream s = new FileStream(file, FileMode.Open))
{
ox = (OrderXml)xs.Deserialize(s);
}
What have I missed that allows serialize to work, but prevents deserialize from working properly?
I've found the problem.
And it has nothing to do with XmlSerilizer.
Up until recently, this program was being used only to write files.
Many (most) of the properties written have getters, but no setters because of the above.
The object's fields are written from deserializing a JSON body of a web request
and the properties written to the xml file are calculated from these.
For example:
[XmlElement(ElementName = "DeliveryNet")]
public decimal DeliveryNet { get { return Math.Round((100M * price_delivery) / (fee.vat_rate + 100M), 2); } set { } }
I just need to make these blank setters actually do something, then it should work fine.
Related
While referencing one of the Stack Overflow posts, XmlSerializer won't serialize IEnumerable, I have the following code that would serialize to XML correctly but when deserialized back, the IEnumerable comes back empty.
using System.Xml.Linq;
using System.Xml.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
public class Hello{
public static void Main(){
// <!--- Serializing part -->
Glossary glossary1 = new Glossary();
glossary1.Term = "Test1";
glossary1.Definition = "Test1";
Glossary glossary2 = new Glossary();
glossary2.Term = "Test2";
glossary2.Definition = "Test2";
List<Glossary> glossaryList = new List<Glossary>();
glossaryList.Add(glossary1);
glossaryList.Add(glossary2);
GlossaryCollection glossary = new GlossaryCollection(glossaryList);
XDocument doc = new XDocument();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(GlossaryCollection));
using (var writer = doc.CreateWriter()) {
xmlSerializer.Serialize(writer, glossary);
}
doc.Save("test.xml");
XDocument doc2 = XDocument.Load("test.xml");
Console.WriteLine(glossary.glossaryList.Count());
Console.WriteLine(doc2.ToString());
//So far looks good - serializing works fine!
// </!--- Serializing part -->
// <!--- De-Serializing part -->
GlossaryCollection temp2;
xmlSerializer = new XmlSerializer(typeof(GlossaryCollection));
using (var reader = doc2.Root.CreateReader()) {
var temp = xmlSerializer.Deserialize(reader);
temp2 = (GlossaryCollection) temp;
}
Console.WriteLine(temp2.glossaryList.Count()); // Results in 0, should be 2... :(
// </!--- De-Serializing part -->
}
[XmlRoot]
public class GlossaryCollection {
[XmlIgnore]
public IEnumerable<Glossary> glossaryList;
[XmlElement]
public List<Glossary> GlossarySurrogate { get { return glossaryList.ToList(); } set { glossaryList = value; } } // https://stackoverflow.com/questions/9102234/xmlserializer-wont-serialize-ienumerable
public GlossaryCollection() {
glossaryList = new List<Glossary>();
}
public GlossaryCollection(IEnumerable<Glossary> glossaryList) {
this.glossaryList = glossaryList;
}
}
[XmlType("Glossary")]
public class Glossary
{
public string Id { get; set; }
public string Term { get; set; }
public string Definition { get; set; }
}
}
Update: Based on Iridium's answer, it does not seem possible to use IEnumerable with XmlSerializer since it was returning a new list everytime, so went with the approach of converting to a List. However if there is a solution (even if hacky) with keeping glossaryList an IEnumerable, do let me know - even if simply for pure curiosity.
When your XML-serialized type has a property of type List<T>, XmlSerializer will get the value of the property, then call .Add(...) to add the deserialized elements (i.e. it does not set the property).
Since the getter for the GlossarySurrogate property returns a new list every time you get it, the changes the XML serializer makes to the result are lost.
If you want this to work correctly, you're going to need to convert your glossaryList field to a List<Glossary> and return it directly from the GlossarySurrogate getter, without calling .ToList() on it.
I am serializing Linq object to using XmlSerializer but I am getting
"There was an error reflecting type
'System.Collections.Generic.List`1"
I have extended the Linq object with Serilizable attributes, something that m I doing incorrect here?
[Serializable]
public partial class Customer
{
}
[Serializable]
public partial class Comment
{
}
[Serializable]
public class CustomerArchive
{
public Customer MyCustomer{ get; set; }
public Comment MyComment { get; set; }
}
internal static void Serialize(IEnumerable<CustomerArchive> archive, string fileName)
{
var serializer = new XmlSerializer(typeof(List<CustomerArchive>)); // Error
using (var stream = File.Create(fileName))
{
using (var sw = new StreamWriter(stream))
using (var writer = new XmlTextWriter(sw)
{
Indentation = 5,
Formatting = Formatting.Indented
})
serializer.Serialize(writer, new List<CustomerArchive>(archive));
}
}
I think you have some properties inside MyCustomer class that cannot be serialized by the XML Serializer. Like a generic Dictionary<> for example.
Put [XmlIgnore] attribute on those properties and try again. If after that your code works you will simply need to make some surrogate properties that would serialize to XML and back without any issues.
So I'm writing my first mvc page, and I'm trying to write a series of routes to allow a reporting system to create simple reports. The xml is small, here is an example:
<xml><root><item><value>23</value></item></root>
I tried this:
using (StringWriter xmlStringWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(xmlStringWriter))
{
XmlWriter.WriteStartElement("root")
...
}
return xmlStringWriter.ToString();
}
but this obviously returns a string and is not interpreted as xml by the browser. I also know* that if you return an object that is serializable then the browser knows to interpret that as xml or json. So I tried defining a set of objects to hold each other in the way the xml is nested:
[Serializable]
public class XmlReportRoot
{
[System.Xml.Serialization.XmlAttribute("root")]
public List<XmlReportItem> item { get; set; }
}
[Serializable]
public class XmlReportItem
{
[System.Xml.Serialization.XmlAttribute("item")]
public XmlReportValue value { get; set; }
}
[Serializable]
public class XmlReportValue
{
[System.Xml.Serialization.XmlAttribute("value")]
public string count { get; set; }
}
and:
XmlReportRoot xmlRoot = new XmlReportRoot();
XmlReportItem xmlItem = new XmlReportItem();
List<XmlReportItem> itemList = new List<XmlReportItem>();
itemList.Add(xmlItem);
XmlReportValue xmlValue = new XmlReportValue();
xmlValue.count = newCustomers.ToString();
xmlItem.value = xmlValue;
xmlRoot.item = itemList;
XmlSerializer xmlSer = new XmlSerializer(typeof(XmlReportRoot));
xmlSer.Serialize(xmlRoot); //this line doesn't work
but this just feels wrong, and I couldn't quite get the serialization to work without worrying about a file stream, which I would rather do.
So I guess I was trying to find a way to do something like XmlWriter but be able to serialize that without an object type and return that, instead of having to worry about custom serializable objects types.
Use XmlWriter.Create(Response.OutputStream) and Response.ContentType = "application/xml"
I am currently working on a project that requires me to output XML from its endpoints along with JSON. I have the following model:
[DataContract(Namespace="http://www.yale.edu/tp/cas")]
[XmlType("serviceResponse")]
[XmlRoot(Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
[XmlElement("authenticationSuccess")]
public AuthenticationSuccess Success { get; set; }
[XmlElement("authenticationFailure")]
public AuthenticationFailure Failure { get; set; }
}
The output is as follows when success is not null:
<serviceResponse>
<authenticationSuccess />
</serviceResponse>
Now, I can see that obviously, I don't have a prefix assigned to the namespace I told the elements to be a part of. My issue is that I cannot find a place to add the namespace prefixes in MVC4 using the media formatter. I have the following in my global.asax:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
GlobalConfiguration.Configuration.Formatters.XmlFormatter.RemoveSerializer(typeof(Models.ServiceResponse));
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SetSerializer(typeof(Models.ServiceResponse), new Infrastructure.NamespaceXmlSerializer(typeof(Models.ServiceResponse)));
I made a custom serializer based on XmlSerializer in an attempt to intercept the writing request and tack the namespace list on there. The issue with this method is that right now I have breakpoints inside every overrideable method and none of them are tripped when serializing which leads me to believe that my serializer isn't being used.
Is there some built in way to accomplish what I want to do or am I stuck re-implementing the XmlMediaTypeFormatter to pass in namespaces when it serializes objects?
As a followup answer: The easiest solution for me was to write my own XmlMediaTypeFormatter. As it turns out, its not nearly as intimidating as I thought.
public class NamespacedXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
const string xmlType = "application/xml";
const string xmlType2 = "text/xml";
public XmlSerializerNamespaces Namespaces { get; private set; }
Dictionary<Type, XmlSerializer> Serializers { get; set; }
public NamespacedXmlMediaTypeFormatter()
: base()
{
this.Namespaces = new XmlSerializerNamespaces();
this.Serializers = new Dictionary<Type, XmlSerializer>();
}
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
lock (this.Serializers)
{
if (!Serializers.ContainsKey(type))
{
var serializer = new XmlSerializer(type);
//we add a new serializer for this type
this.Serializers.Add(type, serializer);
}
}
return Task.Factory.StartNew(() =>
{
XmlSerializer serializer;
lock (this.Serializers)
{
serializer = Serializers[type];
}
var xmlWriter = new XmlTextWriter(writeStream, Encoding.UTF8);
xmlWriter.Namespaces = true;
serializer.Serialize(xmlWriter, value, this.Namespaces);
});
}
}
Here is the formatter as a gist: https://gist.github.com/kcuzner/eef239003d4f99dfacea
The formatter works by exposing the XmlSerializerNamespaces that the XmlSerializer is going to use. This way I can add arbitrary namespaces as needed.
My top model looks as follows:
[XmlRoot("serviceResponse", Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
[XmlElement("authenticationSuccess")]
public CASAuthenticationSuccess Success { get; set; }
[XmlElement("authenticationFailure")]
public CASAuthenticationFailure Failure { get; set; }
}
In my Global.asax I added the following to place my formatter on the top of the list:
var xmlFormatter = new Infrastructure.NamespacedXmlMediaTypeFormatter();
xmlFormatter.Namespaces.Add("cas", "http://www.yale.edu/tp/cas");
GlobalConfiguration.Configuration.Formatters.Insert(0, xmlFormatter);
After adding the formatter and making sure my attributes were set up properly, my XML was properly namespaced.
In my case, I needed to add the cas namespace linking to http://www.yale.edu/tp/cas. For others using this, just change/replicate the Add call to your heart's content adding namespaces.
Among other data, I have the following XML being sent to me:
<jx:Agency xsi:type="nc:OrganizationType1">
<nc:OrganizationAbbreviationText>ABC</nc:OrganizationAbbreviationText>
</jx:Agency>
In my code, I have Agency declared as follows (I also have the class Agency with a single XmlAttribute OrganizationAbbreviationText):
[XmlElement("Agency", Namespace = jx)]
public Agency Agency { get; set; }
But when I attempt to deserialize, I get "The specified type was not recognized: name='OrganizationType1'" along with the namespace=url
Does anyone know how to properly describe the Agency in my structure?
I am not serializing the structure... the file is being sent to me and I'm deserializing it using the following:
Document doc;
using (FileStream fs = new FileStream(pathName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
XmlSerializer ser = new XmlSerializer(typeof(Document));
doc = (Document)ser.Deserialize(fs);
fs.Close();
}
This is why the xml file needs to be described properly in the Document class.
mho, Here's the code for the Agency class:
[XmlElement(Namespace = jx)]
public class Agency
{
[XmlAttribute("OrganizationAbbreviationText", Namespace = nc)]
public string OrganizationAbbreviationText { get; set; }
}
I don't believe OrganizationType1 really comes into play except that I cannot de-serialize the file I'm being sent without addressing it. jx and nc are constant strings containging URLs. I very much appreciate your help.
Thanks
Clearly, you're missing a type that corresponds to nc:OrganizationType1. In this case you can simply reuse Agency class for nc:OrganizationType1 (if Agency is either default type or nc:OrganizationType1 only).
Also OrganizationAbbreviationText in Agency is an XmlElement and not XmlAttribute.
I have simplified your case and created a sample code that works. Please note [XmlType(TypeName = "OrganizationType1", Namespace = "some-name-space")] on the top of Agency class. Hope it helps, happy to explain
[XmlRoot("Agency")]
[XmlType(TypeName = "OrganizationType1", Namespace = "some-name-space")]
public class Agency
{
[XmlElement("OrganizationAbbreviationText", Namespace = "some-name-space")]
public string OrganizationAbbreviationText { get; set; }
}
public class Program
{
[STAThread]
public static void Main()
{
string xml = "<Agency xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xmlns:nc=\"some-name-space\" xsi:type=\"nc:OrganizationType1\">"
+ "<nc:OrganizationAbbreviationText>ABC</nc:OrganizationAbbreviationText>"
+ "</Agency>";
using (StringReader reader = new StringReader(xml))
{
XmlSerializer ser = new XmlSerializer(typeof(Agency));
var agency = (Agency)ser.Deserialize(reader);
Console.WriteLine(agency.OrganizationAbbreviationText);
}
}
}