I'm trying make a program to read a simple xml file into my class. I've been using this previous question as a guide: How to Deserialize XML document
The code runs fine with no exceptions, but for some reason, SCArray.ShortCut = null and count is 0. I'm having trouble debugging this because there are no exceptions.
Is there a way to catch this error (i.e., why it's not reading the xml correctly, or what part of my code is causing it to return null results from reading the xml)?
<?xml version="1.0"?>
<ShortCutsArray>
<Shortcut>
<Name>Item1</Name>
<Path>http://www.example1.com</Path>
</Shortcut>
<Shortcut>
<Name>Item2</Name>
<Path>\\Server\example2\ex2.exe</Path>
</Shortcut>
</ShortCutsArray>
The c# code:
class Program
{
public static void Main(string[] args)
{
string shortcuts_file = #"\\server\ShortcutLocation.xml";
ShortCutsArray SCArray = null;
XmlSerializer serializer = new XmlSerializer(typeof(ShortCutsArray));
StreamReader reader = new StreamReader(shortcuts_file);
SCArray = (ShortCutsArray)serializer.Deserialize(reader);
reader.Close();
}
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("ShortCutsArray")]
public class ShortCutsArray
{
[XmlArray("ShortCutsArray")]
[XmlArrayItem("ShotCut", typeof(ShortCut))]
public ShortCut[] ShortCuts { get; set; }
}
[Serializable()]
public class ShortCut
{
[System.Xml.Serialization.XmlElement("Name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlElement("Path")]
public string Path { get; set; }
}
Typo: ShotCut -> Shortcut (note the lower case c - either making the ShortCut class Shortcut or change you xml to be ShortCut would be better)
You've got the root as ShortCutsArray and the XML array name as ShortCutsArray. You'd need the following xml for it to work:
<ShortCutsArray>
<ShortCutsArray>
<Shortcut>
<Name>Item1</Name>
<Path>http://www.example1.com</Path>
</Shortcut>
<Shortcut>
<Name>Item2</Name>
<Path>\\Server\example2\ex2.exe</Path>
</Shortcut>
</ShortCutsArray>
</ShortCutsArray>
I'm not sure its possible to debug this as there's nothing going wrong you've simply found no elements that match due to the above errors.
Related
Below piece of code is failing and throwing ArgumentException
static void Main(string[] args)
{
string xml = "<root><SourcePatient><Communication>HP:6055550120</Communication></SourcePatient></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var serializedString = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None,true);
var deserialise = serializedString.ToObject<SampleModel>();
}
Models are,
public class SampleModel
{
public SourcePatientModel SourcePatient { get; set; }
}
public class SourcePatientModel
{
public List<string> Communication { get; set; }
}
How to deserialize this? Sometimes Communication node from xml string will have multiple entries
Your current xml is only a single entry
<Communication>HP:6055550120</Communication>
Change your xml input
<Communication><Entry>HP:6055550120</Entry></Communication>
So later when you get multiple entries, they can be processed
<Communication><Entry>HP:6055550120</Entry><Entry>HP:xxxxxxxxx</Entry></Communication>
Your class needs tweaked a bit
if a string [] is acceptable
[XmlArray(ElementName = "Communication")]
[XmlArrayItem(ElementName = "Entry")]
public string[] comm_list // Whatever name you want here
{
get; set;
}
// if you want a list here
// also if your going to do this, realize it creates a new list every time you use it, not the best. (bad practice)
List<string> Communication
{
get => new List<string>(comm_list );
}
otherwise it gets a little complicated
[XmlRoot(ElementName="Communication")]
public class Communication // element name by def
{
[XmlElement(ElementName="Entry")]
public List<string> entry { get; set; }
}
Another possibility, not sure how your multiple entries come in.
If multiple entries look like the following
<Communication>HP:6055550120, HP:7055550120</Communication>
Then you cant have a direct list,
public class SourcePatientModel
{
public string Communication { get; set; }
// which again this creates a list everytime, its better to change your xml to match a tag name for each entry
[XmlIgnore]
public List<string> CommunicationValues { get => Communication.Split(',').ToList();
}
Also this is just typed up code, there may be some typos or compile errors
First I parsed xml to the valid model, then you can convert to json if needed
using (TextReader sr = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(SampleModel));
var schema = (SampleModel)serializer.Deserialize(sr);
}
[Serializable, XmlRoot("root")]
public class SampleModel
{
public SourcePatientModel SourcePatient { get; set; }
}
public class SourcePatientModel
{
[XmlElement("Communication")]
public List<string> Communication { get; set; }
}
I modified your classes to include some XML serialization attributes. (More on how to figure those out later - the short version is that you don't have to figure it out.)
[XmlRoot(ElementName = "SourcePatient")]
public class SourcePatientModel
{
[XmlElement(ElementName = "Communication")]
public List<string> Communication { get; set; }
}
[XmlRoot(ElementName = "root")]
public class SampleModel
{
[XmlElement(ElementName = "SourcePatient")]
public SourcePatientModel SourcePatientModel { get; set; }
}
...and this code deserializes it:
var serializer = new XmlSerializer(typeof(SampleModel));
using var stringReader = new StringReader(xmlString);
SampleModel deserialized = (SampleModel) serializer.Deserialize(stringReader);
Here's a unit test to make sure a test XML string is deserialized with a list of strings as expected. A unit test is a little easier to run and repeat than a console app. Using them makes writing code a lot easier.
[Test]
public void DeserializationTest()
{
string xmlString = #"
<root>
<SourcePatient>
<Communication>A</Communication>
<Communication>B</Communication>
</SourcePatient>
</root>";
var serializer = new XmlSerializer(typeof(SampleModel));
using var stringReader = new StringReader(xmlString);
SampleModel deserialized = (SampleModel) serializer.Deserialize(stringReader);
Assert.AreEqual(2, deserialized.SourcePatientModel.Communication.Count);
}
Here's a key takeaway: You don't need to memorize XML serialization attributes. I don't bother because I find them confusing. Instead, google "XML to Csharp" and you'll find sites like this one. I pasted your XML into that site and let it generate the classes for me. (Then I renamed them so that they matched your question.)
But be sure that you include enough sample data in your XML so that it can generate the classes for you. I made sure there were two Communication elements so it would create a List<string> in the generated class.
Sites like that might not work for extremely complicated XML, but they work for most scenarios, and it's much easier than figuring out how to write the classes ourselves.
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'm quite new to XML serialization/deserialization and I'm wondering how I should apply tags (XmlElement, XmlAttribute etc) to the following objects which I'm writing to XML files, to make it most optimal for me to later use LINQ or similar to get out the data I want. Let me give an example:
[XmlRoot("Root")]
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
Here I've defined some properties which I'm going to write to XML with some values I'm gonna add later. At this point, I've hardcoded in the values in this method:
public static void RootCapabilities()
{
RootObject ro = new RootObject()
{
Services = new Services()
{
TileMapService = new Service()
{
Title = "Title",
href = "http://something"
}
}
};
Which gets me this XML output:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</Root>
My question is if this is a valid way of doing it or if I have to use the 'XmlElement' and 'XmlAttribute' tags to later deserialize this XML file and get the information out of it, that I want (for example 'title').
I haven't been sure how to write this question, so please let me know if it's too vague.
I would not use XML serialization if I were you. Any changes to your schema/object structure and you immediately lose backwards compatibility due to how XML serialization works as a technology.
Rather separate out the XML serialization from the actual class structure - this way you can make changes to the XML/object schema and write proper migrating functions to handle any changes you make in the future.
Rather use the LINQ-to-XML classes to construct your XML document and save it as they are the easiest way in .NET to convert object structures to XML in a decoupled manner.
if the output is what you expect I think that you shouldn't change anything.
You should use Decorators like XmlAttribute or XmlIgnore when you want change the default behavior (e.g don't include a field, include one field as an attribute...)
Their role is obtain full control of the serialization and avoid unexpected behaviors
If you don't want to worry about the nitty-gritty of the serialization, XmlSerializer has always treated me well.
public static void SerializeObject(T obj, string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(file);
s.Serialize(w, obj);
w.Close();
}
public static T DeserializeObject(string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
using (StreamReader r = new StreamReader(file))
{
return (T)s.Deserialize(r);
}
}
This should really only be used with references types though (objects, not primitive types or structs). Deserialize can return null and casting that to a value type will bring nothing but heartache.
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 am using the XmlSerializer, and was wondering if there is any way, using overrides or something to that effect to get the XmlSerializer to output the types of some nodes.
My problem is that I have serialized a byte array.
class MyClass {
public string Name { get; set; }
public byte[] Bytes { get; set; }
}
I am consuming the xml in a generic service.
The service collects the xml as .
<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>
Is there any way to either generate an xsd at runtime, or somehow output something like this.
I cannot change the class I am serializing, but I can apply overrides to the serializer or in some other way control the serialization.
<Bytes xsi:type='BinaryOfSomeKind'>BlahBlah</Bytes>
I need to know that the data is binary somehow.
Thanks
Craig.
If your class is supplied by a third party then you know your properties and property types and you can deduce your XML and XSD from it. You can create your XSD manually or with the help of a XML tool for example XMLSpy (not free BTW) or XMLFox which is free of charge.
If you know the xml is going to be in that format that you put in the question and you have your class ready you can decorate it as such for it to be deserialized.
The Deserialization class:
[XmlTypeAttribute]
[XmlRootAttribute("MyClass")]
public class MyClass
{
[XmlElementAttribute("Name")]
public string Name { get; set; }
[XmlElementAttribute("Bytes")]
public byte[] Bytes { get; set; }
}
The Deserialzation Method
public static object Deserialize(string xml)
{
var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(MyClass));
using (var reader = XmlReader.Create(new StringReader(xml)))
{
return (MyClass)deserializer.Deserialize(reader);
}
}
The Main Method
static void Main(string[] args)
{
string xml = #"<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>";
MyClass obj = (MyClass)Deserialize(xml);
Console.ReadKey();
}
Make sure to add the following using statements:
using System.Xml.Serialization;
using System.Xml;
It deserialized it into an obj with "Test" as the byte array.
If you generate the XSD at run time, then theres no way you can know what properties there are and it would be down to using reflection to test for specific properties, and then subsequently finding out what types they may be, is this what your after?