Populate a class with LINQ query - c#

I need to populate a class using an XML file.
<Ship>
<Name>Base Ship</Name>
<Owner>PG</Owner>
<Aim>
<Type>base</Type>
<Value>10</Value>
<Last>-1</Last>
</Aim>
<Aim>
<Type>cannon</Type>
<Value>10</Value>
<Last>2</Last>
</Aim>
<Dodge>
<Type>base</Type>
<Value>10</Value>
<Last>-1</Last>
</Dodge>
<EmPower>
<Type>base</Type>
<Value>10</Value>
<Last>-1</Last>
</EmPower>
</Ship>
My problem is how to populate a Dictionary<string, CustomStruct>
This is the struct:
public struct Stat
{
public int StatValue { get; set; }
public int StatLast { get; set; }
public Stat(int statValue, int statLast)
{
StatValue = statValue;
StatLast = statLast;
}
}
My LINQ query looks like this:
string loadDataPath = Application.persistentDataPath + "/saveData.xml";
XDocument loadData = XDocument.Load(loadDataPath);
var query = from item in loadData.Elements("Ship")
select new Ship()
{
Name = (string) item.Element("Name"),
Owner = (string) item.Element("Owner"),
Aim = item.Elements("Aim") // <-- Here lies the problem.
// ...
};
For each Aim XElements I need to populate the Aim dictionary using the following method:
Aim TKey = XML Type
Aim TValue.StatValue = XML Value
Aim TValue.StatLast = XML Last

Use the ToDictionary() extension method to achieve what you want:
Aim = item.Elements("Aim").ToDictionary(x => (string)x.Element("Type"), x => new Stat(int.Parse((string)x.Element("Value")), int.Parse((string)x.Element("Last"))))
Also, I had to change struct to class for Stat, in order to make it work.
If you want to use struct you need to modify it a bit:
public struct Stat
{
public int StatValue { get; set; }
public int StatLast { get; set; }
public Stat(int statValue, int statLast) : this()
{
StatValue = statValue;
StatLast = statLast;
}
}
Got this from this answer.

Here is the correct LINQ to XML syntax:
Aim = item.Elements("Aim").ToDictionary(
e => (string)e.Element("Type"),
e => new Stat((int)e.Element("Value"), (int)e.Element("Last")))

Related

Deserializing array of JSONElement back to a concrete list not working when using System.Text.Json

I have a problem when deserializing an object. The object has a property (data) that is a list of JSONElement. I'm doing:
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(result));
var e = doc.RootElement.GetProperty("data");
var data = JsonSerializer.Deserialize<List<MyItem>>(e);
The serialized result variable has the following content:
{
"data":[
{
"id":245,
"number":14,
"name":"Test"
}
],
"totalCount":-1,
"groupCount":-1,
"summary":null
}
And the MyItem class is as follows:
public class MyItem
{
public int Id { get; set; }
public int Number { get; set; }
public string Name { get; set; }
}
The data variable is a list with x items. However all items are empty instances.
What am I doing wrong?
The problem is likely that your data is using lowercase property names which are not translated to the property names in your class with the default deserialization settings.
using System.Text.Json;
dynamic result = new
{
data = new dynamic[] {
new {
id = 245,
number = 14,
name = "Test"
}
},
totalCount = -1,
groupCount = -1
};
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(result));
var e = doc.RootElement.GetProperty("data");
List<MyItem> data = JsonSerializer.Deserialize<List<MyItem>>(e);
Console.WriteLine($"{data.First().Id} {data.First().Number} {data.First().Name}");
The above code won't work with your MyItem class, but try this instead:
public class MyItem
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("number")]
public int Number { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
If it works, either use the JsonPropertyName on all your properties or else consider changing your deserialization options.
Everythig can be done in one string of code. You just need to set PropertyNameCaseInsensitive = true of JsonSerializerOptions. Better to make it in a startup file.
List<MyItem> data = JsonDocument.Parse(json).RootElement.GetProperty("data")
.Deserialize<List<MyItem>>();

Use Linq to return a new object from a nested class

I have a list of elements that contain another list.
public class SiloNode
{
public string Key { get; private set; }
public string Url { get; private set; }
public List<NodeQuery> Queries { get; private set; }
}
public class NodeQuery
{
public string Query { get; private set; }
public int Seq { get; private set; }
}
I need to produce a new list containing all queries, with the corresponding parent url.
An example would be:
https://www.bbc.co.uk, How old is the BBC?
https://www.bbc.co.uk, Where can I find a guide to BBC channels?
https://www.bbc.co.uk, How is the BBC funded?
https://www.channel4.com, More queries about channel 4 ...
The output should be in the form of class LinkMeta.
public class LinkMeta
{
public LinkMeta(string url, string text)
{
Url = url;
Text = text;
}
public string Text { get; private set; }
public string Url { get; private set; }
}
The complete list is contained in: root.Children.
The query below gives me all Query elements but I can't get back to Url.
var filtered = root.Children.SelectMany(x => x.Queries.Select(y => y.Query));
You can achieve it with the below linq.
var filtered = (from snode in root
from qry in snode.Queries
select new LinkMeta(snode.Url, qry.Query)).ToList();
It will return List of LinkMeta. Hope it helps.
Lambda expressions have the same scope as the nested block. This means that the nested lambda expression still has access to the variable 'x'. So, you should be able to do the following:
var filtered = root.SelectMany(x => x.Children.Queries.Select(y => new LinkMeta(x.Url, y.Query));

an array list with n rows and four columns in c#

I need a clear example that shows me how to define a list that has n rows and 4 columns and how to use it. I need a list to save my data like the below image. as you see this could be a dictionary.
You need to create a class with all the above properties
public class Sample
{
public string vocabulary { get; set; }
public string meaning { get; set; }
public int number { get; set; }
public int group { get; set; }
}
and then you can create a List of type Sample,
List<Sample> yourList = new List<Sample>();
You can add items to the list as below
yourList.Add(new Sample { vocabulary = "massive", meaning = "very big", number = 5, group = 15 });
You can access them later like this, if you want the first element,
var result = yourList[0];
this is the easiest and best way of doing it. You need to create a new class and then create new instances of the class and then add it to the list and then use LINQ to get the data out
void Main()
{
var list = new List<myClass>()
list.Add(new myClass() {
Vocabluary = "Vocabluary ",
Meaning = "meaning",
Number = 1,
Group = 2})
}
public class myClass
{
public string Vocabluary { get; set; }
public string Meaning { get; set; }
public int Number { get; set; }
public int Group { get; set; }
}
yes... as Sajeetharan mentioned, with a custom class you can create an any dimensions List. but i don't think you need to think about dimension in C#... it is a bit more high level than that.
just simply create a class and put everything you need in it...
public class CustomClass{
public string d1;
public int d2;
public string d3;
public string d4;
...
//you can easily create a N dimension class
}
to access it and apply it
public void Main(){
List<CustomClass> list = new List<CustomClass>();
CustomClass cc = new CustomClass();
cc.d1 = "v1";
cc.d2 = 0; //v2
list.Add(cc);
//to access it
foreach(CustomClass tmpClass in list)
{
string d1Value = tmpClass.d1;
int d2Value = tmpClass.d2;
}
}

Use LINQ to transform single object into IEnumerable via Properties/reflection?

I have a simple object like such:
public class Foo {
public int One { get; set; }
public int Two { get; set; }
....
public int Eleven { get; set; }
}
Given an IEnumerable, what I want is a LINQ method to transform as such:
myFooEnumerable.Select(n => transformMagicGoesHere);
Where my return object looks like this:
public class Bar {
public string DurationDescription {get;set;} //Value would be "One" or "Two" or ...
public int Value {get;set;} //Holds value in the property One or Two or ...
}
So for every item N in myFooEnumerable in the example above I'd get 11(N) items in my resultant select statement.
This should do it:
var bars = myFooEnumerable.SelectMany(
x => x.GetType().GetProperties().Select(p => new Bar {
DurationDescription = p.Name,
Value = (int)p.GetValue(x)
}));
Not a great thing to be doing in the first place, IMO, but it will at least work.

LINQ XML Read different hierarchies into 1 object

I have an XML file
<searchResponse requestID=“500” status=“success”>
<pso>
<psoID ID=“770e8400-e29b-41d4-a716-446655448549”
targetID=“mezeoAccount”/>
<data>
<email>user2#example.net</email>
<quotaMeg>100</quotaMeg>
<quotaUsed>23</quotaUsed>
<realm>Mezeo</realm>
<path>/san1/</path>
<billing>user2</billing>
<active>true</active>
<unlocked>true</unlocked>
<allowPublic>true</allowPublic>
<bandwidthQuota>1000000000</bandwidthQuota>
<billingDay>1</billingDay>
</data>
</pso>
</searchRequest>
and I want to extract the data into a single business object. Am I better to go
MezeoAccount mcspAccount = new MezeoAccount();
mcspAccount.PsoID = doc.Element("psoID").Attribute("ID").Value;
mcspAccount.Email = doc.Element("email").Value;
...
or build a list even though I know there is only 1 record in the file?
var psoQuery = from pso in doc.Descendants("data")
select new MezeoAccount {
PsoID = pso.Parent.Element("psoID").Attribute("ID").Value,
Email = pso.Element("email").Value,
... };
What would people suggest would be the more correct way, or a better way even, if I missed something.
If you know that your xml only will contain one record of the data in mind you shouldn't create a list for it. So your first example looks fine.
A pattern I personally use is something like this:
public class MezeoAccount
{
public string PsoID { get; set; }
public string Email { get; set; }
public static MezeoAccount CreateFromXml(XmlDocument xml)
{
return new MezeoAccount()
{
PsoID = xml.Element("psoID").Attribute("ID").Value,
Email = doc.Element("email").Value;
};
}
}
//Usage
var mezeoAccount = MezeoAccount.CreateFromXml(xml);
It looks like you didn't get a working answer to this question. Assuming that there can only be one account in the XML file, I would do it like this:
using System;
using System.Linq;
using System.Xml.Linq;
public class MezeoAccount
{
public string PsoId { get; set; }
public string Email { get; set; }
public int QuotaMeg { get; set; }
// Other properties...
}
public class Program
{
public static void Main()
{
XDocument doc = XDocument.Load("input.xml");
XElement pso = doc.Element("searchResponse").Element("pso");
XElement data = pso.Element("data");
MezeoAccount x = new MezeoAccount
{
PsoId = pso.Element("psoID").Attribute("ID").Value,
Email = data.Element("email").Value,
QuotaMeg = int.Parse(data.Element("quotaMeg").Value),
// Other properties...
};
}
}

Categories