Deserialize XML to exact Class only - c#

is there a way to force the XML Deserialize to convert only to an exact matching class object and throw an exception (or error) if it does not match exactly?
The following code will successfully import all 3 xml files:
using System.IO;
using System.Windows;
using System.Xml.Serialization;
namespace XmlDeserialize
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Person data;
XmlSerializer xs = XmlSerializer.FromTypes(new[] { typeof(Person) })[0];
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\exact.xml"))
{
data = (Person)xs.Deserialize(reader);
}
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\reduced.xml"))
{
data = (Person)xs.Deserialize(reader);
}
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\extended.xml"))
{
data = (Person)xs.Deserialize(reader);
}
}
}
public class Person
{
public string Name;
public int Age;
}
}
exact.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
<Age>9999</Age>
</Person>
The exact.xml fits exactly and therefore it should be possible to deserialized it without any problem
reduced.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
</Person>
To be honest, I expected that I will receive an error by using reduced.xml, but it will be deserialized without a problem. I thought that if "Age" is not nullable (int?) I will receive an error and not the default value of an int. Spezially when keeping in mind that the following code will lead to a compiler error.
int x;
MessageBox.Show("x: " + x); //Error: Use of unassigned local variable 'x'
Therefore the successful deserialization is strange for me but somehow explainable....
But what I completely cannot understand is that fact that I do not receive an error when using the following xml file:
extended.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
<Age>9999</Age>
<Address>MyAddress</Address>
</Person>
The deserialization completely ignores that he is not able to assign "Address" to any class property....
Is it possible to force an exact parsing?
Best for me would be to receive an error by using reduced.xml and extended.xml but at least when using extended.xml I should receive an error....
BR,
MUT

Related

Error while DeSerializing XML with namespaces to C# Object

I have an XML file which starts something like this:-
<?xml version="1.0" encoding="UTF-8"?>
<Deal xmlns="http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AccountingDate>2019-09-30</AccountingDate>
When I try to convert this object to XML like below, I get an error:-
private static void Prod_Error_Test()
{
string prodRequestXml = File.ReadAllText("ProdXml.xml");
var serializer = new XmlSerializer(typeof(Service.Deal));
Service.Deal request ;
var reader = new StringReader(prodRequestXml);
request = (Service.Deal)serializer.Deserialize(reader);
}
The Error Message is "There is an error in XML document (2, 2).". The Inner Exception Message is "<Deal xmlns='http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models'> was not expected."
Service.Deal is a WCF Proxy.So I may not be able to add any attributes. Can anyone suggest what to do here ?
Being a WCF proxy doesn't preclude adding attributes; in particular, it is usually a partial class, which means you can have your own separate file, with:
namespace Service
{
[XmlRoot("Deal", Namespace = "http://schemas.datacontract.org/2004/07/DealioCapLinkLib.Dealio.Models")]
partial class Deal {}
}
But ultimately: if the type doesn't conveniently fit the XML: stop fighting it - create a new separate type that fits the XML and works well with XmlSerializer, and then map between the two types in your own code.

How to make serialized data compact?

i wrote an application which is a custom console that allows execution of various commands. One of the commands allows serialization of data. The input data is a string, which is a list of comma separated values.
My question is - how to make the serialized data compact as much as possible?
The serialization format is not important for me.
Here is the command's code:
using CustomConsole.Common;
using System.IO;
using System.Xml.Serialization;
using System;
namespace Shell_Commander.Commands
{
public class SerializeCommand : ICommand
{
private string _serializeCommandName = "serialize";
public string Name { get { return this._serializeCommandName; } set { _serializeCommandName = value; } }
public string Execute(string parameters)
{
try
{
var splittedParameters = parameters.Split(" ");
var dataToSerialize = splittedParameters[0].Split(",");
var pathTofile = splittedParameters[1].Replace(#"\", #"\\");
XmlSerializer serializer = new XmlSerializer(dataToSerialize.GetType());
using (StreamWriter writer = new StreamWriter(pathTofile))
{
serializer.Serialize(writer, dataToSerialize);
var length = new FileInfo(pathTofile).Length;
Console.WriteLine($"Wrote file to: {pathTofile}");
return length.ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e);
return "0";
}
}
}
}
The command accepts 2 parameters:
Data to serialize
File path (in order to save the serialized data).
Example - for the "1,2,4" input, the following file will be saved:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>4</string>
</ArrayOfString>
EDIT:
I want my command to be able to serialize also complex objects in the future, so writing the string as is to the file is not a solution.
I want to use only standard serialization methods and formats.

C# Functions to accept arguments from XML

This is an extension of a question I had beforehand
I have a specific function that I want to run, and it is located inside an XML File:
Console.WriteLine("Text for test, {0}, {1}", testWord, testWord2);
The text is stored in an XML file:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<world>
<region name="TestRegion">
<area name="TestArea">
<building name="Outside">
<room name="TutorialRoom">
<textToDisplay>"Text for test, {0},{1}"</textToDisplay>
<extraString>testWord,tesWord2</extraString>
</room>
</building>
</area>
</region>
</world>
</root>
I can easily get the string data using LINQ
XElement xelement = XElement.Load("..\\..\\LocationDatabase.xml");
var textToDisplay= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("textToDisplay");
var extraString= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("extraString");
And this works completely fine. The issue I have is when I don't have a word in the XML file, but rather a property of a class. I have a singleton Player, and it has a autoproperty Name. To normally access it, I can just say:
Console.WriteLine("Your name is:", Player.Instance.Name);
But how do I, instead, keep this in the XML file? Like:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<world>
<region name="TestRegion">
<area name="TestArea">
<building name="Outside">
<room name="TutorialRoom">
<textToDisplay>"Your name is: {0}"</textToDisplay>
<extraString>Player.Instance.Name</extraString>
</room>
</building>
</area>
</region>
</world>
</root>
When I use the past command, it simple thinks that whole section is a string, and outputs "Your name is: Player.Instance.Name"
An example using my own code:
The Player Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GuardsOfAetheria
{
class Player
{
public enum Class
{
Melee,
Magic,
Ranged
}
public string Name { get; set; }
public Class PlayerClass { get; set; }
private static readonly Player instance = new Player();
static Player()
{
}
private Player()
{
}
public static Player Instance
{
get
{
return instance;
}
}
}
}
Is there a way to solve this?
EDIT 1:
I was able to do something similar using the following lines of code:
var typ = typeof(Player);
var prop = typ.GetProperty("Name");
var propVal = prop.GetValue(Player.Instance);
Console.WriteLine(((string)textToDisplay.First()).Replace(#"\n", Environment.NewLine), propVal);
This works fine, and gets the necessary data. The issue here is that in different parts, different classes have to be called var typ = typeof(Player), and different instances have to be attributed var propVal = prop.GetValue(Player.Instance). I can store the name of the class and instance I need to get from, but simply using a string that holds that data doesn't work, like below:
string className = "Player";
var typ = typeof(className);
var prop = typ.GetProperty("Name");
var propVal = prop.GetValue(Player.Instance);
Console.WriteLine(((string)textToDisplay.First()).Replace(#"\n", Environment.NewLine), propVal);
Is there anyway to do that?
Type player = Type.GetType("Player");
or
Type player = Type.GetType("myNamespace.Player");
See here for more info
Then you are going to have to get an instance of that type, see manipulating types. Then you will need to get the PropertyInfo's for the type, then get the one you want Instance, etc for Instance object.
You will need to get into intimate detail of telling the compiler what object your working with and what properties you want to access. Reflection is a runtime compiled way of accessing objects. It is definitely more complicated than the run-of-the-mill program.

Serialize C# objects to specific format

I serialize a Vehicle object with the following code to serialize the object:
XmlSerializer serializer = new XmlSerializer(typeof(Vehicle));
using (StreamWriter sw = new StreamWriter(RESULTS_FILE_PATH_))
using (XmlWriter writer = new XmlTextWriter(sw))
{
try
{
serializer.Serialize(writer, vehicles[0]);
}
catch (Exception exception)
{
Console.WriteLine("Exception thrown: " + exception);
}
}
The results are as follows:
<?xml version="1.0" encoding="utf-8"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<modelYear xmlns="urn:configcompare4g.kp.chrome.com">2014</modelYear>
<subdivisionName xmlns="urn:configcompare4g.kp.chrome.com">Buick</subdivisionName><modelId
</Model>
I need the format to be like this:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ModelArrayElement xmlns="urn:configcompare4g.kp.chrome.com">
<model>
<modelYear>2014</modelYear>
<subdivisionName>Buick</subdivisionName>
</model>
</ModelArrayElement>
</S:Body>
</S:Envelope>
Does anyone have any suggestions on how I can produce the proper format?
For such a serialization need to use SoapFormatter.
Detailed information can be found in the description on MSDN
(SoapFormatter).
If your XML output is non-standard and it will be difficult to use any default formatter, I would think about using some templating engine, like RazorEngine, so it would be something like:
string template =
#"<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">"+
"<S:Body>"+
" <ModelArrayElement xmlns="urn:configcompare4g.kp.chrome.com">"+
" <model>"+
" <modelYear>#Model.modelYear</modelYear>"+
" <subdivisionName>#Model.subdivisionName</subdivisionName>"+
" </model>"+
" </ModelArrayElement>"+
"</S:Body>"+
"</S:Envelope>";
var model = vehicles[0];
string result = Razor.Parse(template, model);
This gives a complete control of output and allows to make more complex logic based on loops, conditionals etc.

C# Web Service XmlSerializer issue

I have a C# Web Service that is serializing my simple class:
[Serializable]
[XmlInclude(typeof(Bitmap))]
[XmlTypeAttribute(Namespace = "http://tempuri.org/")]
public class Class1
{
private static Bitmap _myImage = new Bitmap(#"C:\WebApplication1\ClassLibrary1\Untitled.png");
public Bitmap MyImage
{
get { return _myImage; }
set
{
_myImage = value;
}
}
}
Here's the asmx.cs code that does the serialization:
[WebMethod]
public string HelloWorld()
{
var c = new Class1();
XmlSerializer serializer = new XmlSerializer(typeof(Class1));
return XMLSerializer(c);
}
public string XMLSerializer(object pObject)
{
try
{
XmlSerializer xs = new XmlSerializer(pObject.GetType());
using (StringWriter stream = new StringWriter())
{
xs.Serialize(stream, pObject);
stream.Flush();
return stream.ToString();
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
Looks prety straight forward. However, the XML generated by the XmlSerializer is producing and error when I try to DeSerialize it.
{"There is an error in XML document (5, 5)."}
{"Parameter is not valid."}
When I try to load the generated XML into IE I get this error.
Switch from current encoding to specified encoding not supported. Error processing resource 'file:///C:/Users/mhall15/Deskt...
<?xml version="1.0" encoding="utf-16"?>
Here's the generated XML:
<?xml version="1.0" encoding="utf-16"?>
<Class1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyImage xmlns="http://tempuri.org/">
<Palette />
</MyImage>
</Class1>
Any ideas what's going on?
During the serialization, replace "encoding="utf-16" with "encoding="utf-8"" and that will cut it. The source of the problem - I'm not sure, but I've ran into it numerous times, and that's how I dealt with it.
That's how to deal with the IE issue.
The deserialization should be amongst these lines. I'm posting the kind of arbitrary code I normally use:
public static object DeserializeFromXML<T>(string _input)
{
object _temp = Activator.CreateInstance<T>();
Type expected_type = _temp.GetType();
_temp = null;
XmlSerializer serializer = new XmlSerializer(expected_type);
StringReader stringWriter = new StringReader(_input);
object _copy = serializer.Deserialize(stringWriter);
return _copy;
}
In the above example, I'm using templating for reusability sake. You should be able to call the method by saying DeserializeFromXML < Class1 >(_xml_input) where xml input is the string. That will force the compiler to use the definition of the given class to deserialize the XML input. That way you wouldn't even have to change the encoding in the serialization. If it's also a case where you may or may not know the data type to deserialize with, you can use a strategy design pattern where you register the root type of the XML with it's associated generic type. Later on you can reference that registry and arbitrarily deserialize any XML input as long as the root type is registered. It's a trick i normally use as well. If more is needed on this, let me know, and I'll explain in detail.
In addition,if you are running IE 9, the new update to IE 9 makes it difficult to view XML. Press F12 - go to developer tools and change your browser mode to run as IE 8 instead of IE 9.

Categories

Resources