get missing attributes in XML from XmlSerializer - c#

how I can check if an attribute or an element is missing in my xml, when I deserialize an Object from Xml?
XmlSerializer fill missing values with their defaults, but how I can know whether it is the default value or it's missing in the Xml?
I have to know this, because if I publish a new version of my program and have added values to my objects, I want to show a prompt with new (missing) values to the user. He have to know the circumstances.
[Serializable]
public class Dummy
{
public int MyInteger { get; set; }
public string MyString { get; set; }
public double MyDouble { get; set; }
public bool MyBool { get; set; }
public Dummy()
{
//Missing values in the xml would filled up with these values
MyInteger = default(int);
MyString = default(string);
MyDouble = default(double);
MyBool = default(bool);
}
}
class Program
{
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(Dummy));
Dummy dummy = new Dummy(){ MyInteger = 1, MyBool = false, MyDouble = 3.4, MyString="dummy"};
StringBuilder sb = new StringBuilder();
using(StringWriter writer = new StringWriter(sb))
serializer.Serialize(writer, dummy);
/*sb contains:
* <?xml version="1.0" encoding="utf-16"?>
* <Dummy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
* <MyInteger>1</MyInteger>
* <MyString>dummy</MyString>
* <MyDouble>3.4</MyDouble>
* <MyBool>false</MyBool>
* </Dummy>
*/
//xml without MyDouble
string xml = #" <Dummy>
<MyInteger>1</MyInteger>
<MyString>dummy</MyString>
<MyBool>false</MyBool>
</Dummy>";
Dummy readDummy;
using (StringReader reader = new StringReader(xml))
readDummy = (Dummy)serializer.Deserialize(reader);
/*readDummy contains:
* MyInteger = 1,
* MyString = "dummy",
* MyDouble = 0,
* MyBool = false
*/
}
}
Update
Thank you Yeldar Kurmangaliyev for the Schema-Validation.
My current Problem is that the exception which was thrown by the Schema-Validator let me access only MyBool and not the missing value MyDouble.
The Exception-Message contains the name of the property MyDouble, but should I extract the property name from the Exception-Message? It feels very dirty.
Here is the updated code:
[Serializable]
public class Dummy
{
public int MyInteger { get; set; }
public string MyString { get; set; }
public double MyDouble { get; set; }
public bool MyBool { get; set; }
public Dummy()
{
//Missing values in the xml would filled up with these values
MyInteger = default(int);
MyString = default(string);
MyDouble = default(double);
MyBool = default(bool);
}
}
class Program
{
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(Dummy));
string xml = #" <Dummy>
<MyInteger>1</MyInteger>
<MyString>dummy</MyString>
<MyBool>false</MyBool>
</Dummy>";
Dummy readDummy;
XmlReaderSettings settings = new XmlReaderSettings() { ValidationType = ValidationType.Schema };
settings.Schemas.Add(GetXmlSchemas(typeof(Dummy)).First());
settings.ValidationEventHandler += (s, e) =>
{
//I got an exception with the sender "MyBool". How I can reach the variable "MyDouble" which is missing?
};
using (StringReader reader = new StringReader(xml))
using (XmlReader xmlReader = XmlReader.Create(reader, settings))
readDummy = (Dummy)serializer.Deserialize(xmlReader);
}
public static XmlSchemas GetXmlSchemas(Type type)
{
var schemas = new XmlSchemas();
var exporter = new XmlSchemaExporter(schemas);
var mapping = new XmlReflectionImporter().ImportTypeMapping(type);
exporter.ExportTypeMapping(mapping);
return schemas;
}
}

You can use XmlReaderSettings class for initializing and using XmlReader over a StringReader. However, you will need an XSD schema.
That's a proper way to validate XML on deserializing.
Take a look at XmlReaderSettings. Maybe, you will find an easier way to do this :)
string xml = #" <Dummy>
<MyInteger>1</MyInteger>
<MyString>dummy</MyString>
<MyBool>false</MyBool>
</Dummy>";
Dummy readDummy;
XmlSchemaSet schemas = null; // here is your schema
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(schemas);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += (s, e) =>
{
throw e.Exception; // Here you go
};
using (StringReader reader = new StringReader(xml))
using (XmlReader xmlReader = XmlReader.Create(reader, settings))
readDummy = (Dummy)serializer.Deserialize(xmlReader);

Related

file xml inside multiple xml in one line

I have a file .xml inside multiple xml in one line.
How can I read this file and convert to object?
I tried with this code it works if there is only one.
Please help and thank you all
[XmlRoot(ElementName = "DepartmentMaster")]
public class DepartmentMaster
{
[XmlElement(ElementName = "DepartmentId")]
public int DepartmentId { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "Description")]
public string Description { get; set; }
[XmlElement(ElementName = "test")]
public int Test { get; set; }
}
//string xml = "<DepartmentMaster><DepartmentId>267854</DepartmentId><Name>Purchase</Name><Description>Purchase Department</Description><test>1</test></DepartmentMaster>";
string xml = "<DepartmentMaster><DepartmentId>267854</DepartmentId><Name>Purchase</Name><Description>Purchase Department</Description><test>1</test></DepartmentMaster><DepartmentMaster><DepartmentId>267855</DepartmentId><Name>Purchase5</Name><Description>Purchase Department5</Description><test>5</test></DepartmentMaster>";
using (TextReader reader = new StringReader(xml))
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(DepartmentMaster));
var model = (DepartmentMaster)deserializer.Deserialize(reader);
}
image from the database
image from the database
Here it is two approaches below.
The first is using setting to accept XML data with multiple root elements (ConformanceLevel.Fragment).
private static IList<DepartmentMaster> DeserializeFragment(string xml)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
XmlReader reader = XmlReader.Create(new MemoryStream(Encoding.ASCII.GetBytes(xml)), settings);
var serializer = new XmlSerializer(typeof(DepartmentMaster));
var list = new List<DepartmentMaster>();
while (serializer.Deserialize(reader) is DepartmentMaster element)
{
list.Add(element);
}
return list;
}
And the second by adding a root element to deserialize a well-formed XML document.
public class DepartmentMasters
{
[XmlElement("DepartmentMaster")]
public List<DepartmentMaster> Items;
}
private static DepartmentMasters DeserializeWellFormedXML(string xml)
{
var text = #"<?xml version=""1.0""?><DepartmentMasters>" + xml + "</DepartmentMasters>";
var serializer = new XmlSerializer(typeof(DepartmentMasters));
return (DepartmentMasters)serializer.Deserialize(new StringReader(text));
}

XML response coming up with extra tags

I'm working on Web API services and returning XML as response.
Below is my model class
public class School
{
public List<StudentDetails> students {get;set;}
}
public class StudentDetails
{
public string Name {get;set;}
public int Age {get;set;}
}
My controller action method code
School test = new School();
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age=25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age=35;
test.students.Add(s1);
test.students.Add(s2);
return Request.CreateResponse(HttpStatusCode.OK, test);
My XML Response
<School xmln:i="http......> //not typing complete text here
<students>
<StudentDetails>
<Age>25</Age>
<Name>ABC</Name>
</StudentDetails>
<StudentDetails>
<Age>35</Age>
<Name>DEF</Name>
</StudentDetails>
</students>
</School>
Here in response, why am I getting <StudentDetails> tag ? Instead, I'm expecting <students> in place.
You can try to add a class decorations that will change serialized XML element names. Check it out below.
c#, version 1
void Main()
{
const string outputFile = #"e:\temp\SerializedFile.xml";
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age = 25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age = 35;
School school = new School();
school.students = new List<StudentDetails>();
school.students.Add(s1);
school.students.Add(s2);
// save new XML file, serialized from classes
using (StreamWriter sw = new StreamWriter(outputFile))
{
XmlSerializer x = new XmlSerializer(typeof(School));
// to remove not needed namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// create new XML file
x.Serialize(sw, school, ns);
}
Console.WriteLine("Classes were serialized as '{0}' file.", outputFile);
}
[XmlRoot("school")]
public class School
{
[XmlElement(ElementName = "student")]
public List<StudentDetails> students { get; set; }
}
public class StudentDetails
{
public string Name { get; set; }
public int Age { get; set; }
}
Output
<?xml version="1.0" encoding="utf-8"?>
<school>
<student>
<Name>ABC</Name>
<Age>25</Age>
</student>
<student>
<Name>DEF</Name>
<Age>35</Age>
</student>
</school>
c#, version 2
void Main()
{
string outputXML = string.Empty;
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age = 25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age = 35;
School school = new School();
school.students = new List<StudentDetails>();
school.students.Add(s1);
school.students.Add(s2);
// save new XML file, serialized from classes
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IndentChars = "\t";
// to remove BOM
settings.Encoding = new UTF8Encoding(false);
using (var sw = new StringWriter())
{
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
XmlSerializer x = new XmlSerializer(typeof(School));
// to remove not needed namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// create new XML file
x.Serialize(xw, school, ns);
}
outputXML = sw.ToString();
}
Console.WriteLine("Classes were serialized as an XML string:{1}{0}", outputXML, Environment.NewLine);
}
// Define other methods and classes here
[XmlRoot("school")]
public class School
{
[XmlElement(ElementName = "student")]
public List<StudentDetails> students { get; set; }
}
public class StudentDetails
{
public string Name { get; set; }
public int Age { get; set; }
}

Serialize XML from C# class, need to set namespaces

I'm having trouble getting my top-level element to look exactly like this using the XmlSerializer (and C# attributes):
<rootObject xmlns="http://www.example.com/xmlschemas/nonStandardSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/xmlschemas/nonStandardSchema1.xsd">
<otherSerializedObjects />
</rootObject>
Currently, the closest I've gotten is this:
<rootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
d1p1:schemaLocation="http://www.example.com/xmlschemas/nonStandardSchema1.xsd"
xmlns:d1p1="xsi"
xmlns="http://www.example.com/xmlschemas/nonStandardSchema">
<otherSerializedObjects />
</rootObject>
C#
[XmlRoot("rootObject", Namespace = "http://www.example.com/xmlschemas/nonStandardSchema")]
public class RootObject
{
[XmlAttribute("schemaLocation", Namespace = "xsi")]
public string SchemaLocation { get; set; }
[XmlElement("Image")]
public Object[] OtherSerializedObjects { get; set; }
public RootObject()
{
OtherSerializedObjects = new Object[]{};
}
}
public class Program
{
static void Main(string[] args)
{
var rootObject = new RootObject
{
SchemaLocation = "http://www.example.com/xmlschemas/nonStandardSchema1.xsd",
OtherSerializedObjects = new object[]{}
};
var serializer = new XmlSerializer(typeof(RootObject));
var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, rootObject);
Console.WriteLine(stringWriter.ToString());
}
}
Any ideas?
Firstly, the [XmlAttribute(Namespace=X)] attribute on your schemaLocation field/property needs to have the full namespace for the value of X, not the local namespace shortcut. Incidentally, this can be a property rather than a field. Using a field for this purpose wastes memory.
Secondly, to eliminate the standard xmlns:xsd="http://www.w3.org/2001/XMLSchema", use an XmlSerializer.Serialize overload where you pass in an XmlSerializerNamespaces with just the namespaces you want.
Thus:
[XmlRoot("rootObject", Namespace = "http://www.example.com/xmlschemas/nonStandardSchema")]
public class RootObject
{
public static XmlSerializerNamespaces GetAdditionalNamespaces()
{
XmlSerializerNamespaces xsNS = new XmlSerializerNamespaces();
xsNS.Add("", "http://www.example.com/xmlschemas/nonStandardSchema");
xsNS.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
return xsNS;
}
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string XSDSchemaLocation
{
get
{
return "http://www.example.com/xmlschemas/nonStandardSchema1.xsd";
}
set
{
// Do nothing - fake property.
}
}
[XmlElement("Image")]
public Object[] OtherSerializedObjects { get; set; }
public RootObject()
{
OtherSerializedObjects = new Object[]{};
}
}
And then use it like:
var rootObject = new RootObject
{
OtherSerializedObjects = new object[]{}
};
var serializer = new XmlSerializer(typeof(RootObject));
var stringWriter = new StringWriter();
var ns = RootObject.GetAdditionalNamespaces();
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
serializer.Serialize(xmlWriter, rootObject, ns);
}
Console.WriteLine(stringWriter.ToString());
Example fiddle.
Namespace needs to be the entire namespace, not the shortened version.
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
In my testing it automatically used xsi.

Serializing .NET object to JSON

I tried the following code to serialize a .NET object to JSON, but i keep getting blank text. What am I doing wrong?
[DataContract]
public class JsonObject2
{
[DataMember(Name = "field1")]
string field1 { get; set; }
[DataMember(Name = "field2")]
string field2 { get; set; }
[DataMember(Name = "field3")]
string[] test = { "heshan", "perera" };
}
The object, I attempt to serialize and display the resulting JSON string in a message box, but all i get is blank.
MemoryStream s = new MemoryStream();
DataContractJsonSerializer dcjs2 = new DataContractJsonSerializer((typeof(JsonObject2)));
JsonObject2 obj2 = new JsonObject2();
dcjs2.WriteObject(s, obj2);
StreamReader r = new StreamReader(s);
String x = r.ReadToEnd();
MessageBox.Show(x);
Try adding:
s.Position = 0;
just before you create the StreamReader.

XML Deserialization

I have an xml file which is in this format
"<rundate>
<rundateItem>
<LeaveCreditingMonth>2</LeaveCreditingMonth>
<LeaveCreditingYear>2010</LeaveCreditingYear>
<IncludeNoTimesheet>True</IncludeNoTimesheet>
</rundateItem>
</rundate>"
in case i want to deserialize this xml file, what should be the format of the class or the target object of my deserialization?
Currently my class looks like this:
public class rundate
{
string _leaveCreditingMonth;
string _leaveCreditingYear;
string _includeNoTimesheet;
public string LeaveCreditingMonth {get{return _leaveCreditingMonth;}set{ _leaveCreditingMonth = value;}}
public string LeaveCreditingYear {get{return _leaveCreditingYear;}set{ _leaveCreditingYear = value;}}
public string IncludeNoTimesheet {get{return _includeNoTimesheet;}set{ _includeNoTimesheet = value;}}
}
Your class can stay as is (obviously you should change the data types to be appropriate though) - since you have rundate nested in your XML (which implies there can be more than one) I would suggest adding a collection class as follows:
[XmlRoot("rundate")]
public class RundateCollection
{
[XmlElement("rundateItem")]
public List<rundate> Rundates { get; set; }
}
You can test serializing/deserializing your class with your XML as follows:
XmlSerializer serializer = new XmlSerializer(typeof(RundateCollection));
StringWriter sw = new StringWriter();
rundate myRunDate = new rundate() { LeaveCreditingMonth = "A", IncludeNoTimesheet = "B", LeaveCreditingYear = "C" };
RundateCollection ra = new RundateCollection() { Rundates = new List<rundate>() { myRunDate } };
serializer.Serialize(sw, ra);
string xmlSerialized = sw.GetStringBuilder().ToString();
string xml = File.ReadAllText(#"test.xml");
StringReader sr = new StringReader(xml);
var rundateCollection = serializer.Deserialize(sr);
You will see that the collection class is successfully deserialized from your XML and contains one list item of type runlist.
I would design the class like so:
public class Rundate
{
public int LeaveCreditingMonth { get; set;}
public int LeaveCreditingYear { get; set; }
public bool IncludeNoTimesheet { get; set; }
}
Then you can deserialize it like this:
var serializer = new XmlSerializer(typeof(List<Rundate>));
using (var fs = new FileStream("yourfile.xml", FileMode.Open))
{
using (var reader = new XmlTextReader(fs))
{
var rundates = (List<Rundate>)serializer.Deserialize(reader);
}
}

Categories

Resources