I would like to deserialize xml files with the following pattern :
I would like to put the data into a class call Parameter. However, as you can see, I don't knwo by advance how many <Device> I get between <Parameters> ... </Parameters> and the deserialization is not possible with such a DeserializeObjectXMLFile method.
public static Parameters DeserializeObjectXMLFile(String fileToRead)
{
try
{
Parameters parameters = null;
XmlSerializer xs = new XmlSerializer(typeof(Parameters));
using (StreamReader sr = new StreamReader(fileToRead))
{
parameters = xs.Deserialize(sr) as Parameters;
}
return parameters;
}
catch (Exception ex)
{
Logger.Error(ex);
throw ex;
}
}
I imagined having a property List<Device> Devices {get; set} in my Parameters class but deserialization won't work and I don't know how I can do it.
Could you help me in finding a solution ?
Best regards.
RĂ©mi
Use the XmlDocument object in the System.Xml namespace to parse the XML instead of serializing it.
Here is an example:
var xDoc = new System.Xml.XmlDocument();
xDoc.Load(pathToXml);
foreach (System.Xml.XmlNode node in xDoc.DocumentElement.ChildNodes)
{
var attrName = node.Attributes[0].Name;
var attrValue = node.Attributes[0].Value;
Device dev = new Device(attrName, attrValue);
deviceList.Add(dev);
}
While parsing you can then create a new instance of Parameters and add it to a List object.
Related
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.
I am getting xml data from web api controller in c#. I need to show this data as list in html view page. I tried various methods but none of them is working.
Here is the XML Data which i am getting from api. Suggest a way how it can be done.
Thanks in advance.
<ArrayOfIDValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/IFlicksAPI.Controllers">
<IDValue>
<ID>1</ID>
<Name>Shuba</Name>
</IDValue>
<IDValue>
<ID>2</ID>
<Name>Raji</Name>
</IDValue>
<IDValue>
<ID>3</ID>
<Name>Renu</Name>
</IDValue>
</ArrayOfIDValue>
This way you can parse the xml and iterate through all the items
success: function(xml) {
$(xml).find('IDValue').each(function(){
var id = $(this).find("ID").text();
var name = $(this).find("name").text();
$("#list").append("<option value='" + id + "'>" + name + "</option>")
});
}
ofcource you need to use jQuery ajax function with your web api url
You can create a class using xml.serialiation and serialize the xml data in to an object of that class,
then just fill the list as it must be done
example: the xml template class
using System.Xml.Serialization;
namespace App
{
[XmlRoot("GuiConfig")]
public class ConfigParameters
{
[XmlElement("field1")]
public string field1;
[XmlElement("field2")]
public string field2;
}
how to serialize
public static void GetXmlData()
{
config = new ConfigParameters(); //global scope var
try
{
if (File.Exists("C:/path/config.xml"))
{
String xmlDoc = XDocument.Load("C:/path/config.xml").ToString();
XmlSerializer serializer = new
XmlSerializer(typeof(ConfigParameters));
using (TextReader reader = new StringReader(xmlDoc))
{
mainForm.config =
(ConfigParameters)serializer.Deserialize(reader);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
I have the following XML file called file.xml:
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config">
<defaults>
<serializer>pof</serializer>
</defaults>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>broadcast-data|position</cache-name>
<scheme-name>broadcast</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|position-audit</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|trade</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
</cache-config>
I'm trying to get the inner text of all the cache names, which exist under each cache-mapping node, and put them all in a list. I have this Model.cs class to do that.
class Model
{
private XmlDocument cacheFile = new XmlDocument();
private List<string> cacheNames = new List<string>();
private int nameCount = 0;
public Model()
{
this.loadNames();
}
public void loadNames()
{
try //exception handling
{
cacheFile.Load("../../resources/file.xml");
}
catch (System.IO.FileNotFoundException)
{
Debug.WriteLine("File not found!");
Environment.Exit(1);
}
catch (System.ArgumentException)
{
Debug.WriteLine("Invalid path!");
Environment.Exit(1);
}
catch (Exception e)
{
Debug.WriteLine("Exception thrown!");
Debug.WriteLine(e);
Environment.Exit(1);
}
//get cache names
XmlNodeList nodes = cacheFile.SelectNodes("/cache-config/caching-scheme-mapping/cache-mapping");
foreach (XmlNode node in nodes)
{
string name = node.FirstChild.InnerText;
cacheNames.Add(name);
nameCount++;
}
}
//accessors
public List<string> getCacheNames()
{
return cacheNames;
}
public int getNameCount()
{
return nameCount;
}
}
However, every time I create a Model object and then check if the List was loaded up, it tells me the list is empty! It appears as though the foreach loop never actually runs, or as if the program can't find the nodes I'm specifying. Please help.
If you use LINQ to XML, this is really quite simple:
XNamespace ns = "http://xmlns.oracle.com/coherence/coherence-cache-config";
var doc = XDocument.Load("../../resources/file.xml");
cacheNames = doc.Descendants(ns + "cache-name").Select(e => e.Value).ToList();
You don't need to keep a separate count of items, you can get this from the list:
cacheNames.Count;
As an aside, idiomatic C# uses pascal casing for methods and properties, so if you stuck to this your methods would start with a capital letter - e.g. GetCacheNames.
This has been puzzling many and has been asked many times here in SO. Your XML has default namespace here :
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
Descendant elements inherit ancestor default namespace, unless otherwise specified (using explicit namespace prefix or having local default namespace that point to different namespace URI). Using XmlDocument you can use XmlNamespaceManager to register prefix to namespace URI mapping, and use the registered prefix properly in your XPath query, for example :
var nsMgr = new XmlNamespaceManager(new NameTable());
nsMgr.AddNamespace("d", "http://xmlns.oracle.com/coherence/coherence-cache-config");
var xpath = "/d:cache-config/d:caching-scheme-mapping/d:cache-mapping";
XmlNodeList nodes = cacheFile.SelectNodes(xpath, nsMgr);
Anyway, if you have just started this, switching to newer class XDocument would be a better option.
I have an application that uses namespaces to help deserialize objects stored in XML. The XML namespace is also the C# namespace where the object resides. For example, given the following XML snip:
<xml-config xmlns:app="MyAppNS">
<app:Person>
<Name>Bill</Name>
<Car>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</app:Person>
<app:Person>
<Name>Jane</Name>
<Car>
<Make>VW</Make>
<Model>Jetta</Model>
</Car>
</app:Person>
<app:Car>
<Make>Audi</Make>
<Model>A6</Model>
</app:Car>
</xml-config>
The configuration is really just a random bag of objects. As you can see, there is a mix of Person and Car objects at the top level. I use the namespace to determine the object type at load time for proper deserialization. Here is the code for loading the document:
public static void LoadFile(String file) {
XmlDocument doc = new XmlDocument();
doc.Load(file);
XmlNode root = doc.DocumentElement;
foreach (XmlNode child in root.ChildNodes) {
if (child.NodeType != XmlNodeType.Element) {
continue;
}
Object obj = LoadObject(child);
// TODO handle object here...
}
}
private static Object LoadObject(XmlNode node) {
Object obj = null;
StringBuilder str = new StringBuilder();
if ((node.Prefix != null) && (node.Prefix != "")) {
str.Append(node.NamespaceURI).Append('.');
}
str.Append(node.LocalName);
Assembly assy = Assembly.GetExecutingAssembly();
Type type = assy.GetType(str.ToString());
XmlSerializer serdes = new XmlSerializer(type);
using (XmlNodeReader reader = new XmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
return obj;
}
You many see the issue right away, but when this code is used, the resulting Person objects are empty, however there are no errors. The only way to get the deserializer to populate the child elements is to use the same namespace for the child elements:
<app:Person>
<app:Name>Bill</app:Name>
<app:Car>
<app:Make>Honda</app:Make>
<app:Model>Accord</app:Model>
</app:Car>
</app:Person>
The only other option I have tried is to use fully-qualified type names as the element name (no namespaces), however I haven't had any luck with that. Is there a way to cause the deserializer to accept the non-prefixed children of the XML node?
I finally found a method to accomplish this, modifying the answer found here.
I created a custom deserializer that replicates the namespace for the starting node:
public class CustomXmlNodeReader : XmlNodeReader {
private String _namespace;
public CustomXmlNodeReader(XmlNode node) : base(node) {
_namespace = node.NamespaceURI;
}
public override String NamespaceURI {
get { return _namespace; }
}
}
Using this reader, I am able to load stored objects with the following:
using (XmlNodeReader reader = new CustomXmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
I know this is a bit bad form for XML (forcing the application of a specific namespace), however it suits my needs quite nicely. Answered here in case it helps anyone else.
The HR-XML 3.0 spec provides WSDL's to generate their entities. I'm trying to deserialize their example xml in their documentation, but it's not working.
Candidate.CandidateType candidate = null;
string path = "c:\\candidate.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Candidate.CandidateType), "http://www.hr-xml.org/3");
StreamReader reader = null;
reader = new StreamReader(path);
candidate = (Candidate.CandidateType)serializer.Deserialize(reader);
The error I'm getting:
"<Candidate xmlns='http://www.hr-xml.org/3'> was not expected."
Any suggestions?
Update: I tried XmlSerializing a CandidatePerson element and it looks like it uses CandidatePersonType instead of CandidatePerson. I think I'm doing something wrong here though...
first lines of Candidate.CandidateType (all auto-generated):
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.hr-xml.org/3")]
public partial class CandidateType {
private IdentifierType2 documentIDField;
private IdentifierType2[] alternateDocumentIDField;
private string documentSequenceField;
The following is more of a comment, but it's too long, so I'll put it here.
The CandidateType class is properly decorated with the XmlType attribute. That is an attribute that applies to types, and determines how the type will be emitted in any generated XML Schema. It has nothing to do with the namespace on an element that happens to have the same type.
Consider the following C# code:
public class CandidateType {}
public class Foo
{
CandidateType _candidate1;
CandidateType _candidate2;
}
Note that you can have multiple variables of the same type. In the same way, you could have:
<xs:element name="Candidate1" type="hrx:CandidateType"/>
<xs:element name="Candidate2" type="hrx:CandidateType"/>
These are two elements which will validate against the same type definition, but which are otherwise unrelated. If they are in the same XML Schema, then they will be in the same namespace. But what if they're not? Then you could have an instance document like:
<ns1:Candidate1 xmlns:ns1="namespace1" xmlns="http://www.hr-xml.org/3"> ... </ns1:Candidate1>
<ns2:Candidate2 xmlns:ns2="namespace2" xmlns="http://www.hr-xml.org/3"> ... </ns1:Candidate2>
What you need to do is specify the namespace of the Candidate element to the XML Serializer. The fact that the CandidateType type is in a particular namespace does not determine the namespace of the Candidate element.
Muahaha I figured it out finally!
John Saunders was right, I needed to specify the default namespace in the XmlSerializer, but in addition to that I have to specify the XmlRootAttribute because the Class that I'm trying to de-serialize to does not have the same name as the root element.
Here is my code for de-serializing the HR-XML ProcessCandidate example:
protected static ImportTest.CandidateService.ProcessCandidateType DeserializeProcessCandidate(string path)
{
CandidateService.ProcessCandidateType processCandidate = null;
XmlRootAttribute root = new XmlRootAttribute("ProcessCandidate");
XmlSerializer serializer = new XmlSerializer(typeof(CandidateService.ProcessCandidateType), new XmlAttributeOverrides(), new Type[0], root, "http://www.hr-xml.org/3");
StreamReader reader = null;
try
{
reader = new StreamReader(path);
processCandidate = (CandidateService.ProcessCandidateType)serializer.Deserialize(reader);
reader.Close();
}
catch (Exception ex)
{
reader.Close();
throw (new Exception(ex.InnerException.Message));
}
return processCandidate;
}
Thanks for the help John!