How to set value in nested LINQ select to parent select result? - c#

This is my LINQ:
var sdEnumTypes = from et in sdXmlEnums.Elements("Enum")
select new SdEnum
{
Name = et.Attribute("name").Value,
EnumGuid = et.Attribute("guid").Value,
Enumerators = (from en in et.Elements("Enumerator")
select new SdEnumerator
{
DisplayName = en.Attribute("displayName").Value,
Name = en.Attribute("name").Value
//Enum = I want this to point to parent SdEnum
}).ToList()
};
I want to set (commented part) Enum value to SdEnum that is being selected on level up. How to do it?
This is corresponding XML that is being parsed for you to visualize what I want to do:
<Enums>
<Enum name="Color" guid="{2C68F947-3103-4F3C-9855-60F289B3A039}">
<Enumerator name="Red" displayName="Red Color"/>
<Enumerator name="Green" displayName="Green Color" />
<Enumerator name="Blue" displayName="BlueColor"/>
</Enum>
</Enums>
P.S.
Is there any way to simplify this this query?

Here is a method using XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input =
"<Enums>" +
"<Enum name=\"Color\" guid=\"{2C68F947-3103-4F3C-9855-60F289B3A039}\">" +
"<Enumerator name=\"Red\" displayName=\"Red Color\"/>" +
"<Enumerator name=\"Green\" displayName=\"Green Color\" />" +
"<Enumerator name=\"Blue\" displayName=\"BlueColor\"/>" +
"</Enum>" +
"</Enums>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("Enum").Select(x => new {
name = x.Attribute("name").Value,
guid = x.Attribute("guid").Value,
enumerator = x.Elements("Enumerator").Select(y => new {
name = y.Attribute("name").Value,
displayName = y.Attribute("displayName").Value,
parent = x
}).ToList()
}).ToList();
}
}
}
​

The simplest way to do this is to use another projection:
var result = sdEnumTypes.Select(e =>
{
e.Enumerators.ForEach(enumerator => enumerator.Enum = e);
return e;
});

Related

cannot figure out XML structure using c# and XDocument

I have tried for hours on this.
I need to access the data in the top section to get the SenderCode
Then I need to step through all of the RecipientDeliveries sections and get the RecipientCode then the name/address info for that one.
To try and get the deliveries I have tried this (along with about 100 variations).
No error, but no data is returned ("Enumeration yielded no results")
In the below what I need to do if have code that steps through each of the Recipient sections, grab the 'RecipientCode' for that section and then get the various names and addresses.
I can get the specific SenderCode by doing this:
string xmlText;
using (var memoryStream = new MemoryStream())
{
ASN_Blob.DownloadToStream(memoryStream);
xmlText = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());
}
var document = XDocument.Parse(xmlText);
var aw2 = "http://www.omnicare.com/schema/AdvancedShippingNotices.xsd";
var SenderCode = document.Descendants(XName.Get("SenderCode",aw2)).First().Value;
But keep running into a wall past that.
Here is the XML I need to decode:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:AdvancedShippingNotices xmlns:ns0="http://www.omnicare.com/schema/AdvancedShippingNotices.xsd">
<ns0:ASNID>4129114</ns0:ASNID>
<ns0:CourierID>4SAMEDAY</ns0:CourierID>
<ns0:SenderCode>598</ns0:SenderCode>
<ns0:SenderNameAndAddress>
<ns0:Name>Customer of San Diego</ns0:Name>
<ns0:Address>
<ns0:Line1>5601 Oberlin Drive, Suite 124</ns0:Line1>
<ns0:CityTownOrLocality>San Diego</ns0:CityTownOrLocality>
<ns0:StateOrProvince>CA</ns0:StateOrProvince>
<ns0:PostalCode>92121-3709</ns0:PostalCode>
</ns0:Address>
</ns0:SenderNameAndAddress>
<ns0:RecipientDeliveries>
<ns0:Recipient>
<ns0:RecipientCode>1019</ns0:RecipientCode>
<ns0:RecipientNameAndAddress>
<ns0:Name>VILLAGE SQUARE HEALTHCARE CTR</ns0:Name>
<ns0:Address>
<ns0:Line1>1586 W SAN MARCOS BLVD</ns0:Line1>
<ns0:CityTownOrLocality>SAN MARCOS</ns0:CityTownOrLocality>
<ns0:StateOrProvince>CA</ns0:StateOrProvince>
<ns0:PostalCode>92069</ns0:PostalCode>
</ns0:Address>
</ns0:RecipientNameAndAddress>
<ns0:Deliveries>
<ns0:Delivery>
<ns0:DeliveryID>8930798-5</ns0:DeliveryID>
<ns0:DeliveryType>ROUTE</ns0:DeliveryType>
<ns0:DeliveryRoute>R0130</ns0:DeliveryRoute>
<ns0:ToteID>S5-278</ns0:ToteID>
<ns0:NursingStation>2</ns0:NursingStation>
</ns0:Delivery>
</ns0:Deliveries>
</ns0:Recipient>
<ns0:Recipient>
<ns0:RecipientCode>20366</ns0:RecipientCode>
<ns0:RecipientNameAndAddress>
<ns0:Name>OAKMONT OF ESCONDIDO HILLS</ns0:Name>
<ns0:Address>
<ns0:Line1>3012 BEAR VALLEY PKWY</ns0:Line1>
<ns0:CityTownOrLocality>ESCONDIDO</ns0:CityTownOrLocality>
<ns0:StateOrProvince>CA</ns0:StateOrProvince>
<ns0:PostalCode>92025</ns0:PostalCode>
</ns0:Address>
</ns0:RecipientNameAndAddress>
<ns0:Deliveries>
<ns0:Delivery>
<ns0:DeliveryID>8930798-4</ns0:DeliveryID>
<ns0:DeliveryType>ROUTE</ns0:DeliveryType>
<ns0:DeliveryRoute>R0130</ns0:DeliveryRoute>
<ns0:ToteID>F1-101</ns0:ToteID>
<ns0:NursingStation>AL</ns0:NursingStation>
</ns0:Delivery>
</ns0:Deliveries>
</ns0:Recipient>
</ns0:RecipientDeliveries>
</ns0:AdvancedShippingNotices>
UPDATE:
There is a good working solution that I accepted. But I also came up with this which was working also. I am posting it here just in case it helps somebody else.
//go get the file pointed to by the message in the Blob Storage
CloudBlockBlob ASN_Blob = getBlockBlobByName(fileName, storageAccount, blobContainerName);
string xmlText;
using (var memoryStream = new MemoryStream())
{
ASN_Blob.DownloadToStream(memoryStream);
xmlText = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());
}
//use a dataset
DataSet Ds = new DataSet();
System.IO.StringReader xmlSR1 = new System.IO.StringReader(xmlText);
Ds.ReadXml(xmlSR1);
//get all of the individual totes or items
var Deliveries = (from rec in Ds.Tables["Delivery"].AsEnumerable()
select new
{
fir = rec[0],
sec = rec[1],
thi = rec[2],
forth = rec[3],
nursingStation = rec[4],
delId = Convert.ToInt16( rec[5])
}).ToArray();
//combine them -- needs to be expanded still
var comb3 = (from recipient in Ds.Tables["Recipient"].AsEnumerable()
join recName in Ds.Tables["RecipientNameAndAddress"].AsEnumerable() on recipient[1] equals recName[1]
join address in Ds.Tables["Address"].AsEnumerable() on recipient[1] equals address[6]
select new
{
recipientName = recName[0],
receipientCode = recipient[0],
add1 = address[0],
Id = recName[1]
}
).ToArray();
//prove out that everythying is there
foreach (var stop in comb3)
{
Console.WriteLine($"name: {stop.recipientName} address: {stop.add1}");
//get all the items for this stop
var items = (from i in Deliveries where i.delId == Convert.ToInt16(stop.Id) select i).ToArray();
foreach (var tote in items)
{
Console.WriteLine($"DeliveryId: {tote.fir} Type:{tote.sec} Nursing Station: -{tote.nursingStation}-");
}
}
Using xml linq (XDocument) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns0 = doc.Root.GetNamespaceOfPrefix("ns0");
XElement sender = doc.Descendants(ns0 + "SenderNameAndAddress").FirstOrDefault();
string[] senderAddress = sender.Descendants(ns0 + "Address").Elements().Select(x => (string)x).ToArray();
XElement recipientDeliveries = doc.Descendants(ns0 + "RecipientDeliveries").FirstOrDefault();
var results = recipientDeliveries.Elements(ns0 + "Recipient").Select(x => new
{
name = (string)x.Descendants(ns0 + "Name").FirstOrDefault(),
address = x.Descendants(ns0 + "Address").Elements().Select(y => (string)y).ToArray(),
deliveryID = (string)x.Descendants(ns0 + "DeliveryID").FirstOrDefault(),
deliveryType = (string)x.Descendants(ns0 + "DeliveryType").FirstOrDefault(),
deliveryRoute = (string)x.Descendants(ns0 + "DeliveryRoute").FirstOrDefault(),
toteID = (string)x.Descendants(ns0 + "ToteID").FirstOrDefault(),
nursingStation = (string)x.Descendants(ns0 + "NursingStation").FirstOrDefault()
}).ToList();
}
}
}
Try something like this...works on my machine after creating an xml file from your snippet.
XElement dataFromXML = XElement.Parse(xmlText);
XNamespace aw = "http://www.xxxxxx.com/schema/AdvancedShippingNotices.xsd";
var textSegs = dataFromXML.Descendants(aw + "RecipientDeliveries");
IEnumerable<XElement> textSegs = dataFromXML.Descendants(aw + "RecipientDeliveries");
var recipients = textSegs.Descendants(aw + "RecipientCode");
var address = recipients.Select(x => x.NextNode).FirstOrDefault();
I would also recommend in the future to post a small but valid xml snippet, saves those helping you from having to do so.

How to get the node value by passing type in XDocument C#

I have below XML.
<subscription>
<subscription_add_ons type="array">
<subscription_add_on>
<add_on_code>premium_support</add_on_code>
<name>Premium Support</name>
<quantity type="integer">1</quantity>
<unit_amount_in_cents type="integer">15000</unit_amount_in_cents>
<add_on_type>fixed</add_on_type>
<usage_percentage nil="true"></usage_percentage>
<measured_unit_id nil="true"></measured_unit_id>
</subscription_add_on>
</subscription_add_ons>
My XMLParse function
public XNode GetXmlNodes(XElement xml, string elementName)
{
List<string> addOnCodes= new List<string>();
//elementName = "subscription_add_ons ";
var addOns = xml.DescendantNodes().Where(x => x.Parent.Name == elementName).FirstOrDefault();
foreach (XNode addOn in addOns)
{
//Needed to do something like this
/*var len = "add_on_code".Length + 2;
var sIndex = addOn.ToString().IndexOf("<add_on_code>") + len;
var eIndex = addOn.ToString().IndexOf("</add_on_code>");
var addOnCode = addOn.ToString().Substring(sIndex, (eIndex - sIndex)).Trim().ToLower();
addOnCodes.Add(addOnCode);*/
}
As mentioned in comments by #JonSkeet, I updated my snippet as below.
var addOns = xml.Descendants(elementName).Single().Elements();
foreach (XNode addOn in addOns)
{
/*addon = {<subscription_add_on>
<add_on_code>premium_support</add_on_code>
<name>Premium Support</name>
<quantity type="integer">1</quantity>
<unit_amount_in_cents type="integer">15000</unit_amount_in_cents>
<add_on_type>fixed</add_on_type>
<usage_percentage nil="true"></usage_percentage>
<measured_unit_id nil="true"></measured_unit_id>
</subscription_add_on>} */
//how to get the addOnCode node value ?
var addOnCode = string.Empty;
addOnCodes.Add(addOnCode);
}
But what I need is from the passed XML, get all the nodes of type subscription_add_on then get the value contained in add_on_code & add it to string collection.
Or in general get the value of node by passing type ? Tried with the available methods coming from VS Intellisense but not getting the exact method that can do this?
Thanks!
Here is solution with Xml Linq (XDOCUMENT) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication107
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("subscription_add_on").Select(x => new
{
add_on_code = (string)x.Element("add_on_code"),
name = (string)x.Element("name"),
quantity = (int)x.Element("quantity"),
amount = (int)x.Element("unit_amount_in_cents"),
add_on_type = (string)x.Element("add_on_type")
}).ToList();
}
}
}

C# XML multiple subchild

Good day! Im trying to parse XML subchild using dataset. The thing is its not reading the "SiteCode" when it has multiple value.
for example:
string filePath = #"" + _clsPathIntervalSttngs.localPath + "/" + "hehe.xml";
DataSet dataSet = new DataSet()
dataSet.ReadXml(filePath, XmlReadMode.InferSchema);
// Then display informations to test
foreach (DataTable table in dataSet.Tables)
{
Console.WriteLine(table);
for (int i = 0; i < table.Columns.Count; ++i)
{
Console.Write("\t" + table.Columns[i].ColumnName.Substring(0, Math.Min(6, table.Columns[i].ColumnName.Length)));
Console.WriteLine();
}
foreach (var row in table.AsEnumerable())
{
for (int i = 0; i < table.Columns.Count; ++i)
{
Console.Write("\t" + row[i]);
}
Console.WriteLine();
}
}
this is what it is returning.
Its returning a 0 value and selecting the Product instead of sitecode.
Where did i go wrong?
You might have to check the code because I just took something similar I had lying around and changed it to look at your document hierarchy. I also didn't use a DataSet. Consider the following code:
var filePath = "<path to your file.xml>";
var xml = XDocument.Load(filePath);
var items = from item in xml.Descendants("Product").Elements()
select item.Value;
Array.ForEach(items.ToArray(), Console.WriteLine);
That should show you the values of each element under product. If you want the whole element, remove the .Value in the select clause of the LINQ query.
Update
I'm now projecting to an anonymous type. You'll get one of these for each Product element in the file.
var items = from item in dataset.Descendants("Product")
select new
{
RefCode = item.Element("RefCode").Value,
Codes = string.Join(", ", item.Elements("SiteCode").Select(x => x.Value)),
Status = item.Element("Status").Value
};
Array.ForEach(items.ToArray(), Console.WriteLine);
I have flattened the codes to a comma separated string but you can keep the IEnumerable or ToList it as you wish.
Using xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication51
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XElement doc = XElement.Load(FILENAME);
List<Product> products = doc.Descendants("Product").Select(x => new Product()
{
refCode = (string)x.Element("RefCode"),
siteCode = x.Elements("SiteCode").Select(y => (int)y).ToArray(),
status = (string)x.Element("Status")
}).ToList();
}
}
public class Product
{
public string refCode { get; set; }
public int[] siteCode { get; set; }
public string status { get; set; }
}
}

xml parsing - code refactoring issue

I have the following xml:
<?xml version="1.0" encoding="utf-8"?>
<RootData>
<PassResult>
<FirstOne>P1</FirstOne>
<SecondOne>P2</SecondOne>
<IsMale>false</IsMale>
</PassResult>
<TestResult>
<MarkOne>100</MarkOne>
<MarkTwo>50</MarkTwo>
<Slope>30</Slope>
</TestResult>
<ToneTestResult>
<TotalTime>2</TotalTime>
<CorrectPercentage>90</CorrectPercentage>
</ToneTestResult>
<QuestionnaireResult Version="1">
<Question Id="50">
<Answer Id="B" />
</Question>
<Question Id="60">
<Answer Id="A" />
</Question>
</QuestionnaireResult>
</RootData>
I have the following code which is not at all looking a good one. Lots of repeatative link queries.
How can I refactor this code in a more structured way to fill "OutputData" object. I do not want to change it to XmlSerializer now :-(.
Sample code:
// Gets the root element decendants
var elementRootData = xDocument.Descendants("RootData");
var xElements = elementRootData as IList<XElement> ?? elementRootData.ToList();
// Read first leaf node "ProfileResult"
var passResult = from xElement in xElements.Descendants("PassResult")
select new
{
FirstOne = xElement.Element("FirstOne").GetValue(),
SecondOne = xElement.Element("SecondOne").GetValue(),
IsMale = xElement.Element("IsMale").GetValue()
};
// Read second leaf note
var testResult = from xElement in xElements.Descendants("TestResult")
select new
{
MarkOne = xElement.Element("MarkOne").GetValue(),
MarkTwo = xElement.Element("MarkTwo").GetValue(),
Slope = xElement.Element("Slope").GetValue()
};
// Update OutputData object
var parseOutputData = new OutputData();
foreach (var result in passResult)
{
parseOutputData.FirstOne = result.FirstOne;
parseOutputData.SecondOne = result.SecondOne;
parseOutputData.IsMale = result.IsMale.Equals("True");
}
foreach (var result in testResult)
{
parseOutputData.MarkOne = double.Parse(result.MarkOne);
parseOutputData.MarkTwo = double.Parse(result.MarkTwo);
parseOutputData.Slope = double.Parse(result.Slope);
}
I have to write some more code like this to fill other elements data like ToneTestResult, QuestionnaireResult etc.
Can someone suggest with a sample code?
Best regards,
Given your XML is tiny, you probably don't have to worry too much about performance. You can just do the whole thing in one go, making use of the built in explicit conversions:
var data = new OutputData
{
FirstOne = (string) doc.Descendants("FirstOne").Single(),
SecondOne = (string) doc.Descendants("SecondOne").Single(),
IsMale = (bool) doc.Descendants("IsMale").Single(),
MarkOne = (double) doc.Descendants("MarkOne").Single(),
MarkTwo = (double) doc.Descendants("MarkTwo").Single(),
Slope = (double) doc.Descendants("Slope").Single()
};
As an aside, Descendants will never return anything implementing IList<XElement>, so you can definitely remove that.
Try XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var root = doc.Elements("RootData").Select(x => new
{
passResults = x.Elements("PassResult").Select(y => new
{
firstOne = (string)y.Element("FirstOne"),
secondOne = (string)y.Element("SecondOne"),
isMale = (Boolean)y.Element("IsMale")
}).FirstOrDefault(),
testResult = x.Elements("TestResult").Select(y => new {
markOne = (int)y.Element("MarkOne"),
markTwo = (int)y.Element("MarkTwo"),
slope = (int)y.Element("Slope")
}).FirstOrDefault(),
toneTestResult = x.Elements("ToneTestResult").Select(y => new {
totalTime = (int)y.Element("TotalTime"),
correctPecentage = (int)y.Element("CorrectPercentage")
}).FirstOrDefault(),
questionnaireResult = x.Elements("QuestionnaireResult").Elements("Question").Select(y => new {
question = (int)y.Attribute("Id"),
answer = (string)y.Descendants("Answer").FirstOrDefault().Attribute("Id")
}).ToList(),
}).FirstOrDefault();
}
}
}

Read a XML file using LINQ

How do I read a XML file (XMLfile.xml) using LINQ in a previously made file in the root of a ASP.net program. Each Element exist in the XML file I have already created (see below for an excerpt).
XDocument xmlElements = XDocument.Parse(System.IO.File.ReadAllText(Server.MapPath("XMLfile.xml")));
var elements = from data in xmlElements.Descendants("/NewDataSet/Table")
select new
{
Number0 = (int)data.Element("Number"),
Name0 = (string)data.Element("Name"),
a0 = (double)data.Element("a"),
e0 = (double)data.Element("e"),
i0 = (double)data.Element("i"),
N0 = (double)data.Element("N"),
w0 = (double)data.Element("w"),
Pyrs0 = (double)data.Element("Pyrs"),
mm0 = (double)data.Element("mm"),
MA0 = (double)data.Element("MA0")
};
foreach (var element in elements)
{
m = m + 1;
num[m] = element.Number0;
nam[m] = element.Name0;
a1[m] = element.a0;
ecc[m] = element.e0;
i[m] = element.i0;
N[m] = element.N0;
w[m] = element.w0;
Pyrs[m] = element.Pyrs0;
mm[m] = element.mm0;
MA0[m] = element.MA0;
}
XMLfile.xml
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Table>
<Number>1</Number>
<Name>Ceres</Name>
<a>2.7681117</a>
<e>0.0757544</e>
<i>10.59166</I>
<N>80.3218024</N>
<w>72.73324</w>
<Pyrs>4.61</Pyrs>
<mm>0.2140072</mm>
<MA0>181.38143</MA0>
</Table>
<Table>
<Number>2</Number>
<Name>Pallas</Name>
<a>2.7723622</a>
<e>0.2310236</e>
<i>34.84095</i>
<N>173.0882785</N>
<w>309.98943</w>
<Pyrs>4.62</Pyrs>
<mm>0.2135153</mm>
<MA0>163.60434</MA0>
</Table>
...
</NewDataSet>
What has worked for me is the extension method XPathSelectElements. As in:
var elements = from data in xmlElements.XPathSelectElements("/NewDataSet/Table")
select new
{
Number0 = (int)data.Element("Number"),
Name0 = (string)data.Element("Name"),
a0 = (double)data.Element("a"),
e0 = (double)data.Element("e"),
i0 = (double)data.Element("i"),
N0 = (double)data.Element("N"),
w0 = (double)data.Element("w"),
Pyrs0 = (double)data.Element("Pyrs"),
mm0 = (double)data.Element("mm"),
MA0 = (double)data.Element("MA0")
};
The code below is XML Linq like you requests. XML is case sensitive so you need to change the closing tag "I" to "i". I tested the code below and it works. If you have a string instead of the file then change "Load" to "Parse".
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("Table").Select(x => new
{
number = (int)x.Element("Number"),
name = (string)x.Element("Name"),
a = (double)x.Element("a"),
e = (double)x.Element("e"),
i = (double)x.Element("i"),
n = (double)x.Element("N"),
w = (double)x.Element("w"),
pyrs = (double)x.Element("Pyrs"),
mm = (double)x.Element("mm"),
ma0 = (double)x.Element("MA0")
}).ToList();
}
}
}

Categories