This is my first question on SO, please let me know if I am doing anything wrong!
I am trying to parse an XML similar to this:
<LiveUpdate>
<CityID>F0A21EA2</CityID>
<CityName>CityTown</CityName>
<UserName>john</UserName>
<ApplicationDetails>
<ApplicationDetail
Application="AC"
Licensed="true"
Version="2015.2"
Patch="0001"
/>
<ApplicationDetail
Application="AP"
Licensed="true"
Version="2015.2"
Patch="0002"
/>
</ApplicationDetails>
</LiveUpdate>
I have classes that look like this:
public class Client
{
public string cityID { get; set; }
public string cityName { get; set; }
public string userName { get; set; }
public List<Apps> appList { get; set; }
}
public class Apps
{
public string app { get; set; }
public string licensed { get; set; }
public string version { get; set; }
public string patch { get; set; }
}
I need to be able to have a client class with a list of all the application details to be iterated over.
So far the best I've come up with is:
XDocument xml = XDocument.Load(#"C:\blah\Desktop\1.xml");
var liveUpdate = xml.Root;
var clients = (from e in liveUpdate.Elements()
select new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
appList = e.Elements("ApplicationDetails")
.Select(a => new Apps()
{
app = a.Element("Application").Value,
licensed = a.Element("Licensed").Value,
version = a.Element("Version").Value,
patch = a.Element("Patch").Value
}).ToList()
});
However, I'm currently running into an error that says Object reference not set to an instance of an object.
I've seen some similar examples on here, but not that deal with data before the multiple children.
I'm fairly new to XML and Linq so any help here would be greatly appreciated!
Your XML only contains one LiveUpdate tag, so rather than iterating over all of the elements inside of it, you just want to look at the Root element.
In ApplicationDetails, Application, Licensed and such are attributes, not elements. Use .Attribute() to access them.
ApplicationDetails is a single tag, and inside it you have ApplicationDetail tags.
There is no DateTime element in your LiveUpdate tag.
This works:
var liveUpdate = xml.Root;
var e = liveUpdate;
var clients = new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
//dateTime = e.Element("DateTime").Value,
appList = e.Element("ApplicationDetails").Elements("ApplicationDetail")
.Select(a => new Apps()
{
app = a.Attribute("Application").Value,
licensed = a.Attribute("Licensed").Value,
version = a.Attribute("Version").Value,
patch = a.Attribute("Patch").Value
}).ToList()
};
Since you have already defined a class into which you wish to deserialize, you can use XmlSerializer to deserialize it for you.
First, let's rename some of your property names to more closely match the XML and c# naming conventions:
[XmlRoot("LiveUpdate")]
public class Client
{
public string CityID { get; set; }
public string CityName { get; set; }
public string UserName { get; set; }
[XmlArray("ApplicationDetails")]
[XmlArrayItem("ApplicationDetail")]
public List<Apps> AppList { get; set; }
}
public class Apps
{
[XmlAttribute]
public string Application { get; set; }
[XmlAttribute]
public bool Licensed { get; set; }
[XmlAttribute]
public string Version { get; set; }
[XmlAttribute]
public string Patch { get; set; }
}
Then add the following extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
public static T LoadFromFile<T>(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open))
{
object result = new XmlSerializer(typeof(T)).Deserialize(fs);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
}
Now you can deserialize from your XML file as follows:
string fileName = #"C:\blah\Desktop\1.xml";
var client = XmlSerializationHelper.LoadFromFile<Client>(fileName);
I manually updated your Client class to map correctly to the provided XML, but if you wanted to do it automatically, see here: Generate C# class from XML.
Related
I have and so far failed at implementing 2 approaches to parse an xml tree and cast it, along with its children, into objects. I have tried object serialization as explain here and I have used Linq as explained by the accepted answer here.
With the first approach (deserialization), it works up until the List<ExtensionItem> attribute of IndividualOrEntityValidationExtensions not getting its values assigned (i.e it remains null).
With the second approach (LINQ), I get this error pertaining to the entire OutputFilePaths =... block.
/Users/g-wizkiz/Projects/XmlParser/XmlParser/Program.cs(68,68): Error CS0266: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<XmlParser.models.OutputFilePaths>' to 'XmlParser.models.OutputFilePaths'. An explicit conversion exists (are you missing a cast?) (CS0266) (XmlParser)
I'm happy to go with whichever works, though I do feel like LINQ is a more elegant approach.
I will show my XML and class structure, followed by the respective code blocks.
XML
<?xml version="1.0" encoding="UTF-8" ?>
<ParameterList>
<Parameters>
<NumberOfThreads>10</NumberOfThreads>
<InputFilePath>C:\Input.dat</InputFilePath>
<OutputFilePaths>
<NameFile>name.txt</NameFile>
<ValidationFile>validation.txt</ValidationFile>
<AuditLog>audit.txt</AuditLog>
</OutputFilePaths>
<DictionaryExtensions>
<IndividualOrEntityValidationExtensions>
<ExtensionItem>
<Type>dictType1</Type>
<Path>dictPath1</Path>
</ExtensionItem>
<ExtensionItem>
<Type>dictType2</Type>
<Path>dictPat2</Path>
</ExtensionItem>
</IndividualOrEntityValidationExtensions>
</DictionaryExtensions>
</Parameters>
</ParameterList>
Classes
[XmlRoot("Parameters")]
public class Parameters
{
public int NumberOfThreads { get; set; }
public string InputFilePath { get; set; }
public OutputFilePaths outputFilePaths { get; set; }
public DictionaryExtensions DictionaryExtensions { get; set; }
}
public class OutputFilePaths
{
public string NameFile { get; set; }
public string ValidationFile { get; set; }
public string AuditLog { get; set; }
}
public class DictionaryExtensions
{
[XmlElement("IndividualOrEntityValidationExtensions")]
public IndividualOrEntityValidationExtension IndividualOrEntityValidationExtensions { get; set; }
}
public class IndividualOrEntityValidationExtension
{
[XmlArrayItem("ExtensionItem")]
public List<ExtensionItem> ExtensionItem { get; set; }
}
public class ExtensionItem
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlAttribute("Path")]
public string Path { get; set; }
}
Object deserialization approach
string xmlString = System.IO.File.ReadAllText(#"/Users/g-wizkiz/Projects/XmlParser/XmlParser/parameters.xml");
XmlSerializer serializer = new XmlSerializer(typeof(List<Parameters>), new XmlRootAttribute("ParameterList"));
StringReader stringReader = new StringReader(xmlString);
List<Parameters> parameters = (List<Parameters>)serializer.Deserialize(stringReader)
LINQ approach
XDocument doc = XDocument.Parse(xmlString);
IEnumerable<Parameters> result = from c in doc.Descendants("Parameters")
select new Parameters()
{
NumberOfThreads = (int)c.Attribute("NumberOfThreads"),
InputFilePath = (string)c.Attribute("InputFilePath"),
outputFilePaths = from f in c.Descendants("OutputFilePaths")
select new OutputFilePaths()
{
ValidationFile = (string)f.Attribute("ValidationFile"),
AuditLog = (string)f.Attribute("AuditLog"),
NameFile = (string)f.Attribute("NameFile")
}
};
Cheers!
Testing locally, this works fine as the only change:
public class IndividualOrEntityValidationExtension
{
[XmlElement("ExtensionItem")]
public List<ExtensionItem> ExtensionItem { get; set; }
}
However, you can remove a level in the hierarchy if you prefer - throwing away the IndividualOrEntityValidationExtension type completely:
public class DictionaryExtensions
{
[XmlArray("IndividualOrEntityValidationExtensions")]
[XmlArrayItem("ExtensionItem")]
public List<ExtensionItem> ExtensionItems { get; set; }
}
I'm thinking the problem is related to the Parameter class where the OutputFilePaths is a single entity and not an IEnumerable<OutputFilePaths> or if you are expecting only one outputfilepath then select the .FirstOrDefault() in your linq query (be prepared for null values).
I fixed your linq. The nodes are elements not attributes.
XDocument doc = XDocument.Parse(xmlString);
IEnumerable<Parameters> result = (from c in doc.Descendants("Parameters")
select new Parameters()
{
NumberOfThreads = (int)c.Element("NumberOfThreads"),
InputFilePath = (string)c.Element("InputFilePath"),
outputFilePaths = (from f in c.Descendants("OutputFilePaths")
select new OutputFilePaths()
{
ValidationFile = (string)f.Element("ValidationFile"),
AuditLog = (string)f.Element("AuditLog"),
NameFile = (string)f.Element("NameFile")
}).FirstOrDefault()
}).ToList();
I am still learning how to use LINQ and now I'm struggling with this situation.
I have this XML file (example)
<Results>
<PrxComissao>
<Id>0</Id>
<NumErro>0</NumErro>
<RetCode>0</RetCode>
<IdEvento>0</IdEvento>
<ExecutionTimeMilliseconds>63596143450994.227</ExecutionTimeMilliseconds>
<ExecutionTimeSeconds>63596143450.994225</ExecutionTimeSeconds>
<CodComissao>CFE</CodComissao>
<Montante>20.00</Montante>
<Percentagem>0.0000</Percentagem>
<MntMin>0.00</MntMin>
<MntMax>0.00</MntMax>
<Nome>CFE</Nome>
<Descricao>Custo Factura Estrangeiro</Descricao>
</PrxComissao>
<PrxComissao>
<Id>0</Id>
<NumErro>0</NumErro>
<RetCode>0</RetCode>
<IdEvento>0</IdEvento>
<ExecutionTimeMilliseconds>63596143450994.227</ExecutionTimeMilliseconds>
<ExecutionTimeSeconds>63596143450.994225</ExecutionTimeSeconds>
<CodComissao>CFE</CodComissao>
<Montante>20.00</Montante>
<Percentagem>0.0000</Percentagem>
<MntMin>13.00</MntMin>
<MntMax>123.00</MntMax>
<Nome>CFE</Nome>
<Descricao>www</Descricao>
</PrxComissao>
</Results>
And now what I want to do is get all the XML elements inside the "PrxComissao", and then assign them to my class. This is the code I was trying
XDocument xDoc = XDocument.Parse(resultado);
List<PrxComissao> lstPrxComissao = xDoc.Elements("Results")
.Elements("PrxComissao")
.Elements()
.Select(BL_CartaoCredito.Utils.Data.Converter.FromXElement<PrxComissao>)
.ToList();
ObjAuxResult = lstPrxComissao;
What I am trying to do with this Converter.FromXElement<PrxComissao> is get all that elements and assign them.
Here is my class
public class PrxComissao
{
public string CodComissao { get; set; }
public string Montante { get; set; }
public string Percentagem { get; set; }
public string MntMin { get; set; }
public string MntMax { get; set; }
public string Nome { get; set; }
public string Descricao { get; set; }
public string TipoImposto { get; set; }
public string ComFinanciamento { get; set; }
public string iActivo { get; set; }
public string UtlModificacao { get; set; }
public string DtModificacao { get; set; }
public string UtlCriacao { get; set; }
public string DtCriacao { get; set; }
}
public static T FromXElement<T>(XElement element) where T : class, new()
{
T value = new T();
foreach (var subElement in element.Elements())
{
var field = typeof(T).GetField(subElement.Name.LocalName);
field.SetValue(value, (string)subElement);
}
return value;
}
So now I have two problems. First, I can't get to the elements inside PrxComissao always returns me nothing and then is my LINQ Select correct ? Or is there a better way ?
Start with the Descendants assuming your convertor takes an XElement:
List<PrxComissao> lstPrxComissao = xDoc.Descendants()
.Elements("PrxComissao")
.Select(el => BL_CartaoCredito.Utils.Data.Converter.FromXElement<PrxComissao>(el))
.ToList();
and then (untested) ...
public static T FromXElement<T>(XElement element) where T : class, new()
{
var typeOfT = typeof(T);
T value = new T();
foreach (var subElement in element.Elements())
{
var prop = typeOfT.GetProperty(subElement.Name.LocalName);
if(prop != null)
{
prop.SetValue(value, subElement.Value);
}
}
return value;
}
Currently, your code passes individual child elements of <PrxComissao> to the converter method. I believe you want to pass XElement that references <PrxComissao> instead :
List<PrxComissao> lstPrxComissao =
xDoc.Elements("Results")
.Elements("PrxComissao")
.Select(o => BL_CartaoCredito.Utils
.Data
.Converter
.FromXElement<PrxComissao>(o)
)
.ToList();
Besides, your class uses properties instead of field, so the corresponding reflection method supposed to be used here is GetProperty(), not GetField().
I am working on a wpf application. In this application I received JSON response from server and deserialize it as follow :-
StreamReader streamReader = new StreamReader(jsonResponse.GetResponseStream());
String responseData = streamReader.ReadToEnd();
var myData = JsonConvert.DeserializeObject<List<RootObject>>(responseData);
//UserData ud = new UserData();
foreach (var val in myData)
{
string res = val.response;
if (res == "true")
{
this.Hide();
new lobby().Show();
}
}
My class is as follow :-
public class RootObject
{
public string response { get; set; }
public string user_id { get; set; }
public string username { get; set; }
public string current_balance { get; set; }
public string message { get; set; }
public string oauth_token { get; set; }
public List<string> lastFiveSpinNumbers { get; set; }
}
When I execute this code everything is ok and after checking response lobby.xaml open. Now I need to access values of RootObject class in lobby.xaml.cs. So I created an instance of this class as follow:-
RootObject cd = new RootObject();
UserNameTextBlock.Text = cd.response;
but cd.response is always null. What may be the reason?
You are creating a new instance of RootObject and by default the response property is null.
You can give your lobby class a constructor that takes in a RootObject:
public class lobby
{
public lobby(RootObject rootObject)
{
UserNameTextBlock.Text = rootObject.response;
}
}
Then in your foreach you could do:
if (res == "true")
{
this.Hide();
new lobby(val).Show(); // Pass the root object to the Lobby constructor
}
Note: You may wish to rename lobby to Lobby for your class name, this adheres to better C# naming conventions.
I have a Yaml file:
https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/src/icons.yml
And a class:
public class IconSearch
{
public string Name { get; set; }
public string ClassName { get; set; }
public IEnumerable<string> Filters { get; set; }
}
Can you tell me how I can deserialize the yaml to an IEnumerable of objects?
I expect something like this to work, but it returns null - I'm guessing it's because one of my properties is not the root node (icons). Instead, I'm trying to serialize the children of the root?
var input = new StringReader(reply);
var yaml = new YamlStream();
yaml.Load(input);
var icons = deserializer.Deserialize<IconSearch>(input);
The class you are trying to deserialize to seems to be missing properties.
I went the round about way of converting yaml to json to csharp and this is class that was generated:
public class Rootobject
{
public Icon[] icons { get; set; }
}
public class Icon
{
public string[] categories { get; set; }
public object created { get; set; }
public string[] filter { get; set; }
public string id { get; set; }
public string name { get; set; }
public string unicode { get; set; }
public string[] aliases { get; set; }
public string[] label { get; set; }
public string[] code { get; set; }
public string url { get; set; }
}
Resources used :
YAML to JSON online
JSON to CSHARP (I used Paste special in visual studio)
Use this to deserialize
var icons = deserializer.Deserialize<RootObject>(input);
Update
I have commented out the line that you use to create YamlStream as it is not required (it positions the reader to the end of the stream instead of the beginning, which would explain why you were getting null earlier). Your main method looks as follows and works. I have also fixed the bug that Antoine mentioned
public static void Main()
{
string filePath = "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/src/icons.yml";
WebClient client = new WebClient();
string reply = client.DownloadString(filePath);
var input = new StringReader(reply);
//var yamlStream = new YamlStream();
//yamlStream.Load(input);
Deserializer deserializer = new Deserializer();
//var icons = deserializer.Deserialize<IconSearch>(input);
//Testing my own implementation
//if (icons == null)
// Console.WriteLine("Icons is null");
//Testing Shekhar's suggestion
var root = deserializer.Deserialize<Rootobject>(input);
if (root == null)
Console.WriteLine("Root is null");
}
For Windows 8 application development environment.
Code:
var deserialized = JsonConvert.DeserializeObject<RootObject>(json);
listView.ItemsSource = deserialized; // error
Data model:
public class C
{
public List<Y> programs { get; set; }
public string name { get; set; }
public int code { get; set; }
}
public class RootObject
{
public List<C> cs { get; set; }
public string date { get; set; }
}
public class Y
{
public string category { get; set; }
public string time { get; set; }
public string name { get; set; }
}
What can I do ? I don't find solution.
ItemsSource is looking for an IEnumerable, but you're providing a single object in RootObject. You'd get the same error if you create one of your RootObject instances in code and try the same assignment.
What specifically should be displaying in the list? If you simply change your code to:
listView.ItemsSource = deserialized.cs;
the listView should display your C objects.
I always have trouble figuring out how to go from the serializer output. I do have working code (windows 8 store) that I'm pasting below. It is pretty obvious what it does.
HttpResponseMessage responseGetEmailByPersonsBare =
await clientGetEmailByPersonsBare.PostAsync(UrlBase + EmailDetailGetEmailByPersonsBare, contentGetEmailByPersonsBare);
Stream myStream = await responseGetEmailByPersonsBare.Content.ReadAsStreamAsync();
var djsGetEmailByPersonsBare = new DataContractJsonSerializer(typeof(AEWebDataStructures.RootObjectEmailDetail));
var rootObjectEmailDetail = (AEWebDataStructures.RootObjectEmailDetail)djsGetEmailByPersonsBare.ReadObject(myStream);
responseGetEmailByPersonsBare.EnsureSuccessStatusCode();
returnTaskInfo.EmailDetails = rootObjectEmailDetail.Data;
returnTaskInfo.StatusReturn = AEWebDataStructures.StatusReturn.Success;