Load xml file into multidimensional array - c#

I am pulling my hair out trying to read an XML file into a two dimensional array. I really do not know why the error is appearing. As a matter of fact I have not even done the array part yet.
The XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
-<TERMINAL_MESSAGE_FILE>
-<MESSAGE>
<LABEL>LABEL1</LABEL>
<MSG>MESSAGE1</MSG>
</MESSAGE>
-<MESSAGE>
<LABEL>LABEL2</LABEL>
<MSG>MESSAGE2</MSG>
</MESSAGE>
</TERMINAL_MESSAGE_FILE>
All of the element values come from an array, messageArray[2]. It is possible that I am not creating this right as I am quite new to XML.
Now I am getting the file name from a file dialog control. Here is the code I am using:
class message
{
public string _label{ get; set; }
public string _message{ get; set; }
}
public partial class messageForm : Form
{
InitializeComponent();
}
private loadBTN_Click(object sender, EventArgs e)
{
DialogResult result = openMsgFD.ShowDialog();
if (result == DialogResult.OK)
{
XDocument doc = XDocument.Load(openMsgFD.FileName);
var settingsList = (from element in doc.Root.Elements("MESSAGE")
select new message
{
_label = element.Attribute("LABEL").Value,
_message= element.Attribute("MESSAGE").Value
}).ToList();
// will need to deal with putting this into the array but I haven't got that far yet.
}
}
So the error I get is "Object reference not set to an instance of an object."
Thing is I copied this code from numerous examples that I have seen and some slightly different, but I always get this error. I tried using streamReader and streamWriter but there are carriage returns in the MESSGAE.VALUE so I cannot get that to work.
Any help would be much appreciated. Thanks, Tom

As I hinted in the comment, LABEL and MSG are elements, not attributes.
First up, I would re-define message as below - in idiomatic C#, classes and properties are PascalCased:
public class Message
{
public string Label { get; set; }
public string Msg { get; set; }
}
Then this XLinq should work:
var settingsList = (from element in doc.Root.Elements("MESSAGE")
select new Message
{
Label = (string)element.Element("LABEL"),
Msg = (string)element.Element("MSG")
}).ToList();

Related

CSV files to JSON and then back to CSV

I'm trying to create a page that is going to read a CSV file and create a JSON file. I need two of the columns in the json. The CSV contains between 15,000-30,000 lines. I need the JSON to be max 10,000 lines and then I am going to check the JSON against an API. The result must get values from the original file, and then write it to a new CSV file with all the columns.
The CSV file looks like this:
number,"surname","forename","emailAddress","taxIdentifier"
101719008,"John","smith","smith#hotmail.com","1997xxxxxxxx"
102358612,"John","doe","doe#gmail.com","1993xxxxxxxx"
I have this code to read the CSV file:
protected void Button2_Click(object sender, EventArgs e)
{
string saved = (#"E:\Temp\Spelpaus\Malmö2022fromNeon.csv");
using (var streamreader = new StreamReader(saved))
using (var csvReader = new CsvReader(streamreader, CultureInfo.InvariantCulture))
{
var records = csvReader.GetRecords<RocketLaunch>().ToList();
}
}
public class RocketLaunch
{
public string number { get; set; }
public string surname { get; set; }
public string forename { get; set; }
public string emailAddress { get; set; }
public string taxIdentifier { get; set; }
}
I need a start in the JSON and then 2 columns (number and taxidentifier) from the CSV and then 2 characters at the end.
After I got the answer from the API I need to find number from original list and get all columns for every number from API.
Any ideas?
You've defined CSVEntry.Parse as static, so the call could look something like:
string line = rd.ReadLine();
CSVEntry entry = CSVEntry.Parse(line);
You could add entry to a list of type List<CSVEntry>.
One issue might be that your sample data is comma separated, but your code is splitting on semicolons.
Split is a good method to get you quickly started, but I notice that some fields are quoted, which means there's the potential for the data within the quotes to include commas. I wrote my own CSV parser many years ago to handle this sort of thing, but I'm sure there must be something on NuGet to do this for you these days (look for something that supports RFC 4180).

Json to c# Objects in .NET

I've been wanting to make my own dota 2 stats website/app. Basically, I'm using the steam API to gather all the essential data. Most of this data is stored in Json format.
I've been trying to deserialise the format so that I can have the data in a readable format. Ideally I want to turn it into objects and then put them into a data grid so I can present this data properly to the user.
Additionally, I am using the Portable Steam WebAPI Wrapper for C# and Newtonsoft packages.
Public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
SteamWebAPI.SetGlobalKey("MysteamKey");
var r2 = SteamWebAPI.Game().Dota2().IDOTA2().GetHeroes().GetResponseString(RequestFormat.JSON);
var ds1 = Newtonsoft.Json.JsonConvert.DeserializeObject(r2);
RootObject hero = JsonConvert.DeserializeObject<RootObject>(r2);
Response.Write("Display Hero Data.. </br></br>");
Response.Write( hero.result.heroes );
Response.Write(hero);
}
}
Here is my Hero class: I basically used this website to come up with it - http://json2csharp.com/ Additionally, the Json file can be found here https://api.steampowered.com/IEconDOTA2_570/GetHeroes/v0001/?key=2D13D618DA712015812E970165632F02&language=en_us
public class Hero
{
public string name { get; set; }
public int id { get; set; }
public string localized_name { get; set; }
}
public class Result
{
public List<Hero> heroes { get; set; }
public int status { get; set; }
public int count { get; set; }
}
public class RootObject
{
public Result result { get; set; }
}
Currently, this is what is displayed from my current code:
" Display Hero Data..
System.Collections.Generic.List`1[Hero]RootObject "
It doesn't seem to display any of the data from the json file :/
I'm sure i'm missing something straighforward here, but I just can't put my finger on it :(
I really need some assistence here, If I can get this working, then I can start pulling all the other data I need. I'd appreciate any help whatsoever.
Thanks in advance.
Response.Write( hero.result.heroes );
That is just going to write out the "string" version of heroes. Since it's an object, it's just giving you List's (or Object's!) .ToString() function (which displays System.Collections.Generic.List`1[Hero]RootObject)
You're going to need to iterate over the collection. I see that you're directly writing out to the response, which I would discourage, but, if you want to see these written out with it, you can use something like this:
foreach(var hero in hero.result.heroes)
{
Response.Write(String.Format("<p>Name: {0}, ID: {1}</p>", hero.name, hero.id)
}
Since it looks like you're messing around with webforms, I suggest you take a look at some tutorials on how to use it (or mvc)

C# select data into the viewmodel using linq

I have a viewmodel
public class CompanyDetailVM {
public string CompanyName { get; set; }
public IEnumerable<Comments> Comments { get; set; }
}
I want to retrieve all the data coming from XML, I cant figure out how to get list of comments too (Comments model consist of CommentText, DateTaken). My code looks like:
var model= new CompanyDetailVM
{
CompanyName = detail.Element("result").Element("name").Value,
Comments = new Models.Comments {
CommentText= detail.Element("result").Element("comment").Element("text"),
DateTaken = detail.Element("result").Element("comment").Element("date")
}
}
Error: Cannot implicitly convert type 'Models.Comments' to 'System.Collections.Generic.IEnumerable
I also dont think doing new Models.Comments is the right way. How do I fix the code correctly?
Currently, you assigned a Models.Comment object to a property expecting IEnumerable<Comments> type, which then trigger that conversion error. You can create IEnumerable<Comments> from XML using LINQ like so :
var model= new CompanyDetailVM
{
CompanyName = detail.Element("result").Element("name").Value,
Comments = from comment in detail.Element("result").Elements("comment")
select new Models.Comments
{
CommentText = (string)comment.Element("text"),
DateTaken = (DateTime)comment.Element("date")
}
}
Notice that I use Elements("comment") (with plural Elements) assuming that you may have multiple <comment> elements in the XML source.
If you just have a single Comment in your XML - as it looks like you do here - then the easiest is to do:
var model= new CompanyDetailVM
{
CompanyName = detail.Element("result").Element("name").Value,
Comments = new [] {
new Models.Comments {
CommentText= detail.Element("result").Element("comment").Element("text"),
DateTaken = detail.Element("result").Element("comment").Element("date")
}
}
}
(Note the shorthand array literal syntax - new [] { ... } for easily creating an array (which of course implements IEnumerable<>))
If your XML may contain multiple comments, e.g.
<result>
<name>Test</name>
<comment><text>One</text>...</comment>
<comment><text>Two</text>...</comment>
</result>
Then you might want to move towards using LinqToXML to transform all <comment> tags into Models.Comments objects.

Getting XML attribute values

How I read in the specific value of an XML attribute from a node when my XML looks like the following:
<Settings>
<Display_Settings>
<Screen>
<Name Name="gadg" />
<ScreenTag Tag="asfa" />
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
I only know how to read in the inner text value of XML and not the attribute value. For instance, I want the value of X in LocalPosition. This is what I've tried so far;
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Screen");
foreach (XmlNode nodeInfo in nodeList)
{
XmlNodeList nodeContent = nodeInfo.ChildNodes;
foreach (XmlNode nodeItems in nodeContent)
{
if (nodeItems.Name == "Tag")
{
print("There is a name");
}
if (nodeItems.Name == "LocalPosition")
{
print("TEST");
}
}
}
Though for what I want to do, I think this is the wrong way to go about it. Can someone point in the right direction please?
You can use LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
int x = (int)xdoc.Descendants("LocalPosition").First().Attribute("X");
Or with XPath
int x = (int)xdoc.XPathSelectElement("//LocalPosition").Attribute("X");
string XValue= nodeItems.Attributes["X"].Value; // will solve your problem.
I was a little confused when I first started parsing XML with C# too! Instead of trying to write it all yourself .NET provides System.XML.Linq and System.XML.Serialization to help us with all of the hard stuff!
This approach is slightly different as we:
Load the XML document into a System.XML.Linq.XDocument,
Deserialize the XDocument into .NET objects that we can use as we please =]
I hope you don't mind, but I tweaked your XML example a little bit. Try to avoid using too many attributes within your elements, as they tend to reduce readability. The first line of the XML sample just highlights the version that is being used.
You should be able to copy and paste the code sample below into a new console application to get a feel for how it works. Just make sure your XML file is in the ..\bin\Debug folder (otherwise you will need to provide a full file path reference to it).
As you can see, in this example your XML elements (Display_Settings, Screen, Name, ScreenTag, and LocalPosition) are all classes that we decorate with XMLElement attributes. These classes just have public properties that are automatically populated when we call Deserialize(). Neat!
The same applies to the X, Y and Z attributes.
There are a bunch of tutorials that can explain this a lot better than me - enjoy! =]
XML Example
<?xml version="1.0"?>
<Settings>
<Display_Settings>
<Screen>
<Name>NameGoesHere</Name>
<ScreenTag>ScreenTagGoesHere</ScreenTag>
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
Complete Code Sample
using System;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create XDocument to represent our XML file
XDocument xdoc = XDocument.Load("XmlFile.xml");
// Create a deserializer and break down our XML into c# objects
XmlSerializer deserializer = new XmlSerializer(typeof(Settings));
// Deserialize into a Settings object
Settings settings = (Settings)deserializer.Deserialize(xdoc.CreateReader());
// Check that we have some display settings
if (null != settings.DisplaySettings)
{
Screen screen = settings.DisplaySettings.Screen;
LocalPosition localPosition = screen.LocalPosition;
// Check to make sure we have a screen tag before using it
if (null != screen.ScreenTag)
{
Console.WriteLine("There is a name: " + screen.ScreenTag);
}
// We can just write our integers to the console, as we will get a document error when we
// try to parse the document if they are not provided!
Console.WriteLine("Position: " + localPosition.X + ", " + localPosition.Y + ", " + localPosition.Z);
}
Console.ReadLine();
}
}
[XmlRoot("Settings")]
public class Settings
{
[XmlElement("Display_Settings")]
public DisplaySettings DisplaySettings { get; set; }
}
public class DisplaySettings
{
[XmlElement("Screen")]
public Screen Screen { get; set; }
}
public class Screen
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("ScreenTag")]
public string ScreenTag { get; set; }
[XmlElement("LocalPosition")]
public LocalPosition LocalPosition { get; set; }
}
public class LocalPosition
{
[XmlAttribute("X")]
public int X { get; set; }
[XmlAttribute("Y")]
public int Y { get; set; }
[XmlAttribute("Z")]
public int Z { get; set; }
}
}
Try with nodeItems.Attributes["X"].Value

Object reference not set to an instance of an object - LINQ

I am totally stumped on this one. I am getting a "Object reference not set to an instance of an object." error - but I can't figure out why. This is the code that I have:
public class PlayerProfile
{
public List<Profile> PlayerINfo = new List<Profile>();
public void LoadProfiles(string path)
{
XDocument xmlDoc = XDocument.Load(path);
PlayerINfo = new List<Profile>();
// This is where I get the error:
PlayerINfo = (from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
select new Profile
{
Name = (string)profiles.Element("Name"),
Sex = (string)profiles.Element("Sex"),
Avatar = (string)profiles.Element("Avatar").Attribute("path") ?? "",
Created = (DateTime)profiles.Element("Created")
}).ToList();
}
}
Here is my Profile class:
public class Profile
{
public string Name { get; set; }
public string Sex { get; set; }
public string Avatar { get; set; }
public DateTime Created { get; set; }
}
EDIT - Adding the XML file code:
<?xml version="1.0" encoding="utf-8"?>
<OnlineProfile>
<Player>
<Name>Stacey</Name>
<Sex>Female</Sex>
<Avatar path="/images/Picture.png" />
<Ratio>
<Win>0</Win>
<Loss>0</Loss>
<Abandoned>0</Abandoned>
</Ratio>
<Created>6/19/2011</Created>
</Player>
</OnlineProfile>
from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
This is the problem - OnlineProfile is your root element, just do
from profiles in xmlDoc.Root.Elements("Player")
Do this: from profiles in xmlDoc.Element("OnlineProfile").Elements("Player") instead of from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
From the XML you posted "OnlineProfile" is your Root element, so the child elements you expect arent there.
Tried this out + solved this in visual studio. I see the people above beat me to it. Another way to achieve this is:
xmlDoc.Element("OnlineProfile").Elements("Player")
This is what I posted prior to the Xml becomming available...
here's a good candidate for that error
(string)profiles.Element("Avatar").Attribute("...
").Attribute would cause an error. You need to check for null.
e.g.
= profiles.Element("Avatar") != null ? (string)profiles.Element("Avatar").Attribute("... : null;
do you definitely have an element called Avatar in your Xml file. Did the file load in correctly?

Categories