I need to store xml values into two different tables of a database.
Right now I'm using LINQ, but I can't get it to work as I want.
This is the XML:
<?xml version="1.0" encoding="utf-8" ?>
<nfeProc xmlns="http://www.tst.com">
<NFe xmlns="http://www.tst.com">
<infNFe Id="409" versao="2.00">
<ide>
<cUF>43</cUF>
<cNF>00011740</cNF>
</ide>
<emit>
<GGT>0483</GGT>
</emit>
<det nItem="1">
<prod>
<cProd>27</cProd>
<NCM>85437099</NCM>
</prod>
</det>
<det nItem="2">
<prod>
<cProd>30</cProd>
<NCM>85457099</NCM>
</prod>
</det>
</infNFe>
</NFe>
</nfeProc>
Right now I'm using the following code:
XDocument xml = XDocument.Load(#file);
XNamespace a = "http://www.portalfiscal.inf.br/nfe";
var NFe = from data in xml.Descendants(a + "nfeProc")
select new
{
cUF = (string)data.Elements(a + "NFe").Elements(a + "infNFe").Elements(a + "ide").Elements(a + "cUF").FirstOrDefault(),
nNF = (string)data.Elements(a + "NFe").Elements(a + "infNFe").Elements(a + "ide").Elements(a + "nNF").FirstOrDefault(),
};
var Prod = from Produto in xml.Descendants(a + "det")
select new
{
cProd = (string)Produto.Elements(a + "prod").Elements(a + "cProd").FirstOrDefault(),
NCM = (string)Produto.Elements(a + "prod").Elements(a + "NCM").FirstOrDefault(),
};
foreach (var nf in NFe)
{
}
foreach (var p in prod)
{
}
Then I use ADO.NET to send to the database. But I would like to do in only one foreach, so I could use the OUTPUT clause from SQL Server to relate the two collections(get ID from table 1(NFe) and INSERT into table 2(Prod)). How can I achieve that. I tried a lot of things with no success. I tried in one:
var NFe from data in xml.Descendants(a + "nfeProc")
select new {}...
But this way I only can get one "cProd" and not all of them.
Other approaches are welcome.
Thanks.
EDIT:
Thanks for the answers, but actually I got it to work simply changing my foreach.
foreach (var nf in NFe)
{
foreach (var p in prod)
{
}
}
This way I can use cmd.ExecuteScalar() to get OUTPUT.ID from my first foreach and use it in the next INSERT in my second foreach.
Another way to word your var Prod is
var Prod = xml.Descendants(a + "cProd")
.Select(x => new
{
cProd = (string)x,
NCM = (string)x.Parent.Element(a + "NCM")
});
Or if you absolutely know the NCM node follows the cProd node
NCM = (string)(XElement)x.NextNode
This has been around since .Net 1.1 I guess and no longer in fashion, yet you can:
Create a schema out of your xml file. Without any manual intervention (no element reorganizations, type or namespace customizations) this looks like:
Generate a strongly typed dataset out of your xsd. Or you can use the /c switch for xsd instead of /d and generate classes instead of a typed dataset. Either way you got yourself a data access layer for free. You can read xml into a dataset with one of the DataSet.ReadXml overloads and use that to persist to database or data binding.
Related
I've got a LINQ query in a WCF service that runs and returns the correct number of results that I'm looking for, but repeats the first result 25 times instead of showing me all 25 different records.
The weird thing is that when I take the SQL query that it generates from the debugger and plug it into SQL Management studio, I get the correct results.
I have tried refreshing the view I'm querying from the edmx, and I've tried rewriting the query a few different ways, but I'm starting to run out of ideas.
I've included some of the code below. Any suggestions would be helpful. Thanks!
try
{
using (Entities db = new Entities())
{
var qInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.ToList();
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = new List<StoreItem>();
foreach (var qi in qInventory)
{
resultPInventory.StoreInventory.Add(new StoreItem
{
StoreNum = qi.Store_Number,
Quantity = qi.Curr_Inv
});
}
}
}
catch (Exception e)
{
log.Error("[" + e.TargetSite + "] | " + e.Message);
}
log.Info("ProductInventory(" + inputSKU + ") returned " + resultPInventory.StoreInventory.Count + " results");
return resultPInventory;
As Gert's link in the comments points out, sometimes LINQ may do that if your primary keys are not set up well, or if there are multiple rows with no unique values in your database.
This link also shows a similar problem.
The solution, other than rewriting your database columns (although that would be better on the long run) with better primary / unique keys, is to select specific values anonymously (later you can assign them easily):
var qInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.Select(qi => new { qi.Store_Number, qi.Curr_Inv })
.ToList();
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = new List<StoreItem>();
foreach (var qi in qInventory)
{
resultPInventory.StoreInventory.Add(new StoreItem
{
StoreNum = qi.Store_Number,
Quantity = qi.Curr_Inv
});
}
Of course this won't be the best way if you later need to use qInventory for other things. (In that case you can just select more fields)
PS, here is a way to shorten your code, but I am not sure if LINQ to Entities will allow it, so test it first:
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.Select(qi => new StoreItem { StoreNum = qi.Store_Number, Quantity = qi.Curr_Inv })
.ToList();
Assign new StoreItem() to a variable before adding it.
I'm using LINQ for querying the database. I'm using dynamic keyword in queries. I don't know this dynamic mechanism in depth, so I don't understand what's going on.
The situation follows. The following section of code:
var qGroup = qLocalOrd.GroupBy("new(...)", "it");
var qGroupCast = (qGroup as IQueryable<IGrouping<dynamic,dynamic>>).AsEnumerable();
var qAgg = from ordg in qGroupCast
select new {
ordg.Key,
Agg = (ordg.Key.OsID + " " + ordg.Key.NameOs + "\n" +
(ordg as IEnumerable<dynamic>).Aggregate("Составные части:\n", ...).Trim('\n') + ...)
};
Works just fine. But when I'm adding this to the end:
var qPlain = qAgg.Cast<dynamic>().AsQueryable().Select("new(Key.SubSchet, Key.NameSubSchet, ...)");
qPlain.Dump();
I'm receiving an error like "No field "Key" exists in type "Object"". It's the same if I use
(qAgg as IEnumerable<dynamic>)
So at this point dynamic treatment of qAgg elements is broken somewhy.
Why does this happen and how to make this thing work?
I'm trying to parse an XML file from UN website (http://www.un.org/sc/committees/1267/AQList.xml) using c#.
There is one problem I'm constantly having with this file, and that's the number of child tags varies from one <.INDIVIDUAL.> tag to another. One example is <.FORTH_NAME.> child tag.
I've tried a number of different approaches, but somehow I always seem to be stuck with the same problem, and that's different number of child tags inside <.INDIVIDUAL.> tag.
What I'm trying to achieve is to collect all the tags and their values under one <.INDIVIDUAL.> tag, and then insert only those I want into my database. If a tag is missing, for example <.FOURTH_NAME.>, than I need to insert only first three names into the database, and skip the fourth.
I've tried using Linq to XML, and here are some examples:
XDocument xdoc = XDocument.Load(path);
var tags = (from t in xdoc.Descendants("INDIVIDUALS")
from a in t.Elements("INDIVIDUAL")
select new
{
Tag = a.Name,
val = a.Value
});
foreach (var obj in tags)
{
Console.WriteLine(obj.Tag + " - " + obj.val + "\t");
//insert SQL goes here
}
or:
but this one only collects non empty FOURTH_NAME tags...
var q = (from c in xdoc.Descendants("INDIVIDUAL")
from _1 in c.Elements("FIRST_NAME")
from _2 in c.Elements("SECOND_NAME")
from _3 in c.Elements("THIRD_NAME")
from _4 in c.Elements("FOURTH_NAME")
where _1 != null && _2 != null && _3 != null && _4 != null
select new
{
_1 = c.Element("FIRST_NAME").Value,
_2 = c.Element("SECOND_NAME").Value,
_3 = c.Element("THIRD_NAME").Value,
_4 = c.Element("FOURTH_NAME").Value
});
foreach (var obj in q)
{
Console.WriteLine("Person: " + obj._1 + " - " + obj._2 + " - " + obj._3 + " - " + obj._4);
//insert SQL goes here
}
Any ideas??
Instead of calling Value on the element, consider using a string cast. LINQ to XML safely returns null if the element doesn't exist. Try the following:
var data = XElement.Load(#"http://www.un.org/sc/committees/1267/AQList.xml");
var individuals = data.Descendants("INDIVIDUAL")
.Select(i => new {
First = (string)i.Element("FIRST_NAME"),
Middle = (string)i.Element("SECOND_NAME"),
Last = (string)i.Element("THIRD_NAME")
});
If you want to be more flexible and get all of the name fields, you can do something like the following. (I'll leave the process of grouping individuals as an additional homework assignment ;-)
data.Descendants("INDIVIDUAL").Elements()
.Where (i =>i.Name.LocalName.EndsWith("_NAME" ))
.Select(i => new { FieldName= i.Name.LocalName, Value=i.Value});
Why don't you use XmlSerializer and LINQ instead ?
As explained in this answer, generate your classes by pasting in a new CS file :
menu EDIT > Paste Special > Paste XML As Classes.
Then grab your data as easily as follows :
var serializer = new XmlSerializer(typeof (CONSOLIDATED_LIST));
using (FileStream fileStream = File.OpenRead(#"..\..\aqlist.xml"))
{
var list = serializer.Deserialize(fileStream) as CONSOLIDATED_LIST;
if (list != null)
{
var enumerable = list.INDIVIDUALS.Select(s => new
{
FirstName = s.FIRST_NAME,
SecondName = s.SECOND_NAME,
ThirdName = s.THIRD_NAME,
FourthName = s.FOURTH_NAME
});
}
}
You can then specify any predicate that better suits your needs.
Going this path will be a huge time-saver and less error-prone, no need to use strings to access fields, strong typing etc ...
I have a sharepoint server and I am playing around with it programmatically. One of the applications that I am playing with is Microsoft's Call Center. I queried the list of customers:
if (cList.Title.ToLower().Equals("service requests"))
{
textBox1.Text += "> Service Requests" + Environment.NewLine;
foreach (SPListItem item in cList.Items)
{
textBox1.Text += string.Format(">> {0}{1}", item.Title, Environment.NewLine);
}
}
One of the properties in item is XML. Here is value of one:
<z:row
xmlns:z='#RowsetSchema' ows_ID='1' ows_ContentTypeId='0x0106006324F8B638865542BE98AD18210EB6F4'
ows_ContentType='Contact' ows_Title='Mouse' ows_Modified='2009-08-12 14:53:50' ows_Created='2009-08-12 14:53:50'
ows_Author='1073741823;#System Account' ows_Editor='1073741823;#System Account'
ows_owshiddenversion='1' ows_WorkflowVersion='1' ows__UIVersion='512' ows__UIVersionString='1.0'
ows_Attachments='0' ows__ModerationStatus='0' ows_LinkTitleNoMenu='Mouse' ows_LinkTitle='Mouse'
ows_SelectTitle='1' ows_Order='100.000000000000' ows_GUID='{37A91B6B-B645-446A-8E8D-DA8250635DE1}'
ows_FileRef='1;#Lists/customersList/1_.000' ows_FileDirRef='1;#Lists/customersList'
ows_Last_x0020_Modified='1;#2009-08-12 14:53:50' ows_Created_x0020_Date='1;#2009-08-12 14:53:50'
ows_FSObjType='1;#0' ows_PermMask='0x7fffffffffffffff' ows_FileLeafRef='1;#1_.000'
ows_UniqueId='1;#{28A223E0-100D-49A6-99DA-7947CFC38B18}' ows_ProgId='1;#'
ows_ScopeId='1;#{79BF21FE-0B9A-43B1-9077-C071B61F5588}' ows__EditMenuTableStart='1_.000'
ows__EditMenuTableEnd='1' ows_LinkFilenameNoMenu='1_.000' ows_LinkFilename='1_.000'
ows_ServerUrl='/Lists/customersList/1_.000' ows_EncodedAbsUrl='http://spvm:3333/Lists/customersList/1_.000'
ows_BaseName='1_' ows_MetaInfo='1;#' ows__Level='1' ows__IsCurrentVersion='1' ows_FirstName='Mickey'
ows_FullName='Mickey Mouse' ows_Comments='<div></div>' ows_ServerRedirected='0'
/>
Can I create an XMLnode or some other sort of XML object so that I can easily parse it and pull certain values (these certainties are unknowns right now, since I am just testing right now)?
Thanks SO!
If the XML is valid you could use XmlDocument.LoadXMl like so:
XmlDocument doc = new XmlDocument();
doc.LoadXml(validxmlstring);
You can do this and it should work fine (although I would use the XML document approach Colin mentions, or even better LINQ). You may also find the LINQ extensions in SharePoint Extensions Lib useful.
However, I'm wondering why you would approach it this way instead of using the SPListItem.Item property? It's much simpler to use and very clear. For example:
var title = listItem["Title"]; // Returns title of item
var desc = listItem["Description"]; // Returns value of description field
The only trap is the unusual case of a list that contains a field with an internal name equal to another field's display name. This will always return the value of the field with the internal name first.
Just curious if you have a requirement to go the XML route.
I came up with this code that seems to work ok. Since my XML/C# knowledge is limited I would imagine there is a simpler way:
public void DoParse(string value, string elementname)
{
var split = value.Split((char)39);
XmlDocument xDoc = new XmlDocument();
XmlElement xRoot = xDoc.CreateElement(elementname);
xDoc.AppendChild(xRoot);
for (var i = 0; i < split.Length - 1; i += 2)
{
var attribName = split[i].Replace("=", "").Trim();
var xAttrib = xDoc.CreateAttribute(attribName);
xAttrib.Value = split[i + 1];
xRoot.Attributes.Append(xAttrib);
}
xDoc.Save(string.Format("c:\\xmlout_{0}.xml", elementname));
}
Gives me:
<Customer xmlns:z="#RowsetSchema" ows_ID="1" ows_ContentTypeId="0x0106006324F8B638865542BE98AD18210EB6F4" ows_ContentType="Contact" ows_Title="Mouse" ows_Modified="2009-08-12 14:53:50" ows_Created="2009-08-12 14:53:50" ows_Author="1073741823;#System Account" ows_Editor="1073741823;#System Account" ows_owshiddenversion="1" ows_WorkflowVersion="1" ows__UIVersion="512" ows__UIVersionString="1.0" ows_Attachments="0" ows__ModerationStatus="0" ows_LinkTitleNoMenu="Mouse" ows_LinkTitle="Mouse" ows_SelectTitle="1" ows_Order="100.000000000000" ows_GUID="{37A91B6B-B645-446A-8E8D-DA8250635DE1}" ows_FileRef="1;#Lists/customersList/1_.000" ows_FileDirRef="1;#Lists/customersList" ows_Last_x0020_Modified="1;#2009-08-12 14:53:50" ows_Created_x0020_Date="1;#2009-08-12 14:53:50" ows_FSObjType="1;#0" ows_PermMask="0x7fffffffffffffff" ows_FileLeafRef="1;#1_.000" ows_UniqueId="1;#{28A223E0-100D-49A6-99DA-7947CFC38B18}" ows_ProgId="1;#" ows_ScopeId="1;#{79BF21FE-0B9A-43B1-9077-C071B61F5588}" ows__EditMenuTableStart="1_.000" ows__EditMenuTableEnd="1" ows_LinkFilenameNoMenu="1_.000" ows_LinkFilename="1_.000" ows_ServerUrl="/Lists/customersList/1_.000" ows_EncodedAbsUrl="http://spvm:3333/Lists/customersList/1_.000" ows_BaseName="1_" ows_MetaInfo="1;#" ows__Level="1" ows__IsCurrentVersion="1" ows_FirstName="Mickey" ows_FullName="Mickey Mouse" ows_Comments="<div></div>" ows_ServerRedirected="0" />
Anyone have some input? Thanks.
I want to use the HTML agility pack to parse tables from complex web pages, but I am somehow lost in the object model.
I looked at the link example, but did not find any table data this way.
Can I use XPath to get the tables? I am basically lost after having loaded the data as to how to get the tables. I have done this in Perl before and it was a bit clumsy, but worked. (HTML::TableParser).
I am also happy if one can just shed a light on the right object order for the parsing.
How about something like:
Using HTML Agility Pack
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(#"<html><body><p><table id=""foo""><tr><th>hello</th></tr><tr><td>world</td></tr></table></body></html>");
foreach (HtmlNode table in doc.DocumentNode.SelectNodes("//table")) {
Console.WriteLine("Found: " + table.Id);
foreach (HtmlNode row in table.SelectNodes("tr")) {
Console.WriteLine("row");
foreach (HtmlNode cell in row.SelectNodes("th|td")) {
Console.WriteLine("cell: " + cell.InnerText);
}
}
}
Note that you can make it prettier with LINQ-to-Objects if you want:
var query = from table in doc.DocumentNode.SelectNodes("//table").Cast<HtmlNode>()
from row in table.SelectNodes("tr").Cast<HtmlNode>()
from cell in row.SelectNodes("th|td").Cast<HtmlNode>()
select new {Table = table.Id, CellText = cell.InnerText};
foreach(var cell in query) {
Console.WriteLine("{0}: {1}", cell.Table, cell.CellText);
}
The most simple what I've found to get the XPath for a particular Element is to install FireBug extension for Firefox go to the site/webpage press F12 to bring up firebug; right select and right click the element on the page that you want to query and select "Inspect Element" Firebug will select the element in its IDE then right click the Element in Firebug and choose "Copy XPath" this function will give you the exact XPath Query you need to get the element you want using HTML Agility Library.
I know this is a pretty old question but this was my solution that helped with visualizing the table so you can create a class structure. This is also using the HTML Agility Pack
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(#"<html><body><p><table id=""foo""><tr><th>hello</th></tr><tr><td>world</td></tr></table></body></html>");
var table = doc.DocumentNode.SelectSingleNode("//table");
var tableRows = table.SelectNodes("tr");
var columns = tableRows[0].SelectNodes("th/text()");
for (int i = 1; i < tableRows.Count; i++)
{
for (int e = 0; e < columns.Count; e++)
{
var value = tableRows[i].SelectSingleNode($"td[{e + 1}]");
Console.Write(columns[e].InnerText + ":" + value.InnerText);
}
Console.WriteLine();
}
In my case, there is a single table which happens to be a device list from a router. If you wish to read the table using TR/TH/TD (row, header, data) instead of a matrix as mentioned above, you can do something like the following:
List<TableRow> deviceTable = (from table in document.DocumentNode.SelectNodes(XPathQueries.SELECT_TABLE)
from row in table?.SelectNodes(HtmlBody.TR)
let rows = row.SelectSingleNode(HtmlBody.TR)
where row.FirstChild.OriginalName != null && row.FirstChild.OriginalName.Equals(HtmlBody.T_HEADER)
select new TableRow
{
Header = row.SelectSingleNode(HtmlBody.T_HEADER)?.InnerText,
Data = row.SelectSingleNode(HtmlBody.T_DATA)?.InnerText}).ToList();
}
TableRow is just a simple object with Header and Data as properties.
The approach takes care of null-ness and this case:
<tr>
<td width="28%"> </td>
</tr>
which is row without a header. The HtmlBody object with the constants hanging off of it are probably readily deduced but I apologize for it even still. I came from the world where if you have " in your code, it should either be constant or localizable.
Line from above answer:
HtmlDocument doc = new HtmlDocument();
This doesn't work in VS 2015 C#. You cannot construct an HtmlDocument any more.
Another MS "feature" that makes things more difficult to use. Try HtmlAgilityPack.HtmlWeb and check out this link for some sample code.