I have a complex XML file where I want to retrieve values from specific transaction tag.At the end, i need to take the values to a CSV file.
I am trying to retrieve the value of below in each transaction tags where it is present.
<szCustomerID>2988880562567</szCustomerID>
<szCustomerName>130808125028SHM </szCustomerName>
<szExternalID>3177#06/07/17</szExternalID>
Note that all transaction tag is not the same
Unfortunately it is looping through all transaction tags and i am getting error sequence contains no element
Is there a way i can loop withing the Descendants and always skip the second transaction tag since i won't be needing it
try
{
string strSource_voucher = #"C:\\TempFid";
string[] files = Directory.GetFiles(strSource_voucher, "*.xml", SearchOption.AllDirectories);
if (files.Length > 0) // Check if files has values
{
StringBuilder dataToBeWritten = new StringBuilder();
foreach (string file in files)
{
dataToBeWritten.Append("szCustomerID");
dataToBeWritten.Append(",");
dataToBeWritten.Append("szCustomerName");
dataToBeWritten.Append(",");
dataToBeWritten.Append("szExternalID");
dataToBeWritten.Append(",");
dataToBeWritten.Append(Environment.NewLine);
XDocument xDocument = XDocument.Load(file);
int results = xDocument.Descendants("Transaction").Count();
foreach (var trans in xDocument.Descendants("Transaction"))
{
//var trans = xDocument.Descendants("Transaction");
var val1 = (string)trans.Descendants("Set").Elements("szCustomerID").First();
var val2 = (string)trans.Descendants("Set").Elements("szCustomerName").First();
var val3 = (string)trans.Descendants("Set").Elements("szExternalID").First();
dataToBeWritten.Append(val1);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val2);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val3);
dataToBeWritten.Append(",");
dataToBeWritten.Append(Environment.NewLine);
} // End of For each var trans
Console.WriteLine(dataToBeWritten.ToString());
Console.ReadLine();
You can use Element instead of Elements and check if the value is not null
foreach (var trans in xDocument.Descendants("Transaction"))
{
XElement setElement = trans.Descendants("Set").FirstOrDefault();
if (setElement != null)
{
var val1 = (string)setElement.Element("szCustomerID");
var val2 = (string)setElement.Element("szCustomerName");
var val3 = (string)setElement.Element("szExternalID");
if (val1 != null && val3 != null && val3 != null)
{
dataToBeWritten.Append(val1);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val2);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val3);
dataToBeWritten.Append(",");
dataToBeWritten.Append(Environment.NewLine);
}
}
}
Just use for loop and increasei by 2 like this:
var transactions = xDocument.Descendants("Transaction").ToList();
for (int i = 0; i < transactions.Count(); i += 2)
{
//var trans = xDocument.Descendants("Transaction");
var val1 = (string)transactions[i].Descendants("Set").Elements("szCustomerID").First();
var val2 = (string)transactions[i].Descendants("Set").Elements("szCustomerName").First();
var val3 = (string)transactions.Descendants("Set").Elements("szExternalID").First();
dataToBeWritten.Append(val1);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val2);
dataToBeWritten.Append(",");
dataToBeWritten.Append(val3);
dataToBeWritten.Append(",");
dataToBeWritten.Append(Environment.NewLine);
} // End of For each var trans
There is another way to do this type of conversion, if you are open to use library (Cinchoo ETL - An open source ETL lib)
Stream based, fast to convert any xml to csv format with few lines of codes.
I believe you want to product CSV file from your xml having CUSTOMER table elements only.
Here is how you can do with this library
using (var parser = new ChoXmlReader("sample.xml").WithXPath("/UpdateDB/Transaction")
.WithField("Table", xPath: "/Insert/Table")
.WithField("szCustomerID", xPath: "/Insert/Set/szCustomerID")
.WithField("szCustomerName", xPath: "/Insert/Set/szCustomerName")
.WithField("szExternalID", xPath: "/Insert/Set/szExternalID")
)
{
using (var writer = new ChoCSVWriter("sample.csv").WithFirstLineHeader())
writer.Write(parser.Where(r => r.Table == "CUSTOMER").Select(r => new { szCustomerID = r.szCustomerID, szCustomerName = r.szCustomerName, szExternalID = r.szExternalID }));
}
The output will look as below
szCustomerID,szCustomerName,szExternalID
2988880562567,130808125028SHM,3177#06/07/17
12345,130808125028SHM,3177#06/07/17
UPDATE:
In order to split the szExternalID into 2 columns (szExternalID, szExternalDate), you will have to use the valueconverter to parse and load them. Sample shows how
using (var parser = new ChoXmlReader("sample.xml").WithXPath("/UpdateDB/Transaction")
.WithField("Table", xPath: "/Insert/Table")
.WithField("szCustomerID", xPath: "/Insert/Set/szCustomerID")
.WithField("szCustomerName", xPath: "/Insert/Set/szCustomerName")
.WithField("szExternalID", xPath: "/Insert/Set/szExternalID", valueConverter: (v) => ((string)v).Split('#')[0])
.WithField("szExternalDate", xPath: "/Insert/Set/szExternalID", valueConverter: (v) => ((string)v).Split('#')[1])
)
{
using (var writer = new ChoCSVWriter("sample.csv").WithFirstLineHeader())
writer.Write(parser.Where(r => r.Table == "CUSTOMER").Select(r => new { szCustomerID = r.szCustomerID, szCustomerName = r.szCustomerName, szExternalID = r.szExternalID, szExternalDate = r.szExternalDate }));
}
The new output will look as below
szCustomerID,szCustomerName,szExternalID,szExternalDate
2988880562567,130808125028SHM,3177,06/07/17
12345,130808125028SHM,3177,06/07/17
Disclosure: I'm the author of this library.
Related
I have difficulties understanding this example on how to use facets :
https://lucenenet.apache.org/docs/4.8.0-beta00008/api/Lucene.Net.Demo/Lucene.Net.Demo.Facet.SimpleFacetsExample.html
My goal is to create an index in which each document field have a facet, so that at search time i can choose which facets use to navigate data.
What i am confused about is setup of facets in index creation, to
summarize my question : is index with facets compatibile with
ReferenceManager?
Need DirectoryTaxonomyWriter to be actually written and persisted
on disk or it will embedded into the index itself and is just
temporary? I mean given the code
indexWriter.AddDocument(config.Build(taxoWriter, doc)); of the
example i expect it's temporary and will be embedded into the index (but then the example also show you need the Taxonomy to drill down facet). So can the Taxonomy be tangled in some way with the index so that the are handled althogeter with ReferenceManager?
If is not may i just use the same folder i use for storing index?
Here is a more detailed list of point that confuse me :
In my scenario i am indexing the document asyncrhonously (background process) and then fetching the indext ASAP throught ReferenceManager in ASP.NET application. I hope this way to fetch the index is compatibile with DirectoryTaxonomyWriter needed by facets.
Then i modified the code i write introducing the taxonomy writer as indicated in the example, but i am a bit confused, seems like i can't store DirectoryTaxonomyWriter into the same folder of index because the folder is locked, need i to persist it or it will be embedded into the index (so a RAMDirectory is enougth)? if i need to persist it in a different direcotry, can i safely persist it into subdirectory?
Here the code i am actually using :
private static void BuildIndex (IndexEntry entry)
{
string targetFolder = ConfigurationManager.AppSettings["IndexFolder"] ?? string.Empty;
//** LOG
if (System.IO.Directory.Exists(targetFolder) == false)
{
string message = #"Index folder not found";
_fileLogger.Error(message);
_consoleLogger.Error(message);
return;
}
var metadata = JsonConvert.DeserializeObject<IndexMetadata>(File.ReadAllText(entry.MetdataPath) ?? "{}");
string[] header = new string[0];
List<dynamic> csvRecords = new List<dynamic>();
using (var reader = new StreamReader(entry.DataPath))
{
CsvConfiguration csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture);
csvConfiguration.AllowComments = false;
csvConfiguration.CountBytes = false;
csvConfiguration.Delimiter = ",";
csvConfiguration.DetectColumnCountChanges = false;
csvConfiguration.Encoding = Encoding.UTF8;
csvConfiguration.HasHeaderRecord = true;
csvConfiguration.IgnoreBlankLines = true;
csvConfiguration.HeaderValidated = null;
csvConfiguration.MissingFieldFound = null;
csvConfiguration.TrimOptions = CsvHelper.Configuration.TrimOptions.None;
csvConfiguration.BadDataFound = null;
using (var csvReader = new CsvReader(reader, csvConfiguration))
{
csvReader.Read();
csvReader.ReadHeader();
csvReader.Read();
header = csvReader.HeaderRecord;
csvRecords = csvReader.GetRecords<dynamic>().ToList();
}
}
string targetDirectory = Path.Combine(targetFolder, "Index__" + metadata.Boundle + "__" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + "__" + Path.GetRandomFileName().Substring(0, 6));
System.IO.Directory.CreateDirectory(targetDirectory);
//** LOG
{
string message = #"..creating index : {0}";
_fileLogger.Information(message, targetDirectory);
_consoleLogger.Information(message, targetDirectory);
}
using (var dir = FSDirectory.Open(targetDirectory))
{
using (DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir))
{
Analyzer analyzer = metadata.GetAnalyzer();
var indexConfig = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer);
using (IndexWriter writer = new IndexWriter(dir, indexConfig))
{
long entryNumber = csvRecords.Count();
long index = 0;
long lastPercentage = 0;
foreach (dynamic csvEntry in csvRecords)
{
Document doc = new Document();
IDictionary<string, object> dynamicCsvEntry = (IDictionary<string, object>)csvEntry;
var indexedMetadataFiled = metadata.IdexedFields;
foreach (string headField in header)
{
if (indexedMetadataFiled.ContainsKey(headField) == false || (indexedMetadataFiled[headField].NeedToBeIndexed == false && indexedMetadataFiled[headField].NeedToBeStored == false))
continue;
var field = new Field(headField,
((string)dynamicCsvEntry[headField] ?? string.Empty).ToLower(),
indexedMetadataFiled[headField].NeedToBeStored ? Field.Store.YES : Field.Store.NO,
indexedMetadataFiled[headField].NeedToBeIndexed ? Field.Index.ANALYZED : Field.Index.NO
);
doc.Add(field);
var facetField = new FacetField(headField, (string)dynamicCsvEntry[headField]);
doc.Add(facetField);
}
long percentage = (long)(((decimal)index / (decimal)entryNumber) * 100m);
if (percentage > lastPercentage && percentage % 10 == 0)
{
_consoleLogger.Information($"..indexing {percentage}%..");
lastPercentage = percentage;
}
writer.AddDocument(doc);
index++;
}
writer.Commit();
}
}
}
//** LOG
{
string message = #"Index Created : {0}";
_fileLogger.Information(message, targetDirectory);
_consoleLogger.Information(message, targetDirectory);
}
}
I have a Xamarin (C#) project, where I am trying to loop through some XML, but for some reason my code is not working.
This is what I have now:
DeviceList = new List<DeviceInfo>();
string ResultStatus = "";
string ResultDevice = "";
var result = Encoding.Default.GetString(e.Result);
result = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + result;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(result);
string xPathStatus = "ed_listdevices";
var nodes = xmlDoc.SelectNodes(xPathStatus);
foreach (XmlNode xNode in nodes) {
ResultStatus = xNode.SelectSingleNode("//status").InnerText;
ResultDevice = xNode.SelectSingleNode("//device").InnerText;
}
if (ResultStatus.ToLower() == "ok") {
XmlDocument deviceDoc = new XmlDocument();
deviceDoc.LoadXml(result);
var deviceNodes = deviceDoc.SelectNodes(xPathStatus + "/device");
//foreach(XmlNode dNode in deviceNodes) {
for (int i = 0; i < deviceNodes.Count; i++) {
DeviceList.Add(new DeviceInfo() {
DeviceID = deviceNodes[i].SelectSingleNode("//id").InnerXml,
DeviceName = deviceNodes[i].SelectSingleNode("//name").InnerXml,
DeviceExtraName = "",
DeviceOnlineStatus = deviceNodes[i].SelectSingleNode("//status").InnerXml,
Location = deviceNodes[i].SelectSingleNode("//address").InnerXml,
Time = deviceNodes[i].SelectSingleNode("//time").InnerXml
});
}
}
When I step though the code I get the "ResultStatus" and "ResultDevice" correctly, and when I get to the
for (int i = 0; i < deviceNodes.Count; i++)
the "deviceNodes" variable have a count of 91, and I can see all the individual xml elements that I am suppose to get from the webservice I am calling.
However, when I loop through deviceNodes[i] I only get values from the very first XML element (yes, the value of "i" does change). In other words, my DeviceList is filled with 91 entries with the same values.
Am I missing something obvious?? What am I doing wrong since this isn't working??
PS: I have also tried using a foreach (XmlNode node in deviceNodes) but the result was the same.
"//" in the selector tells it to search from the root node of the document. If you want to search locally under the "current" node, remove the "//"
I have created a XML string and Looping that to get value. But its not entering in foreach loop. But in my other code same loop code is working.
my code is :
XML string:
<SuggestedReadings>
<Suggestion Text="Customer Centricity" Link="http://wdp.wharton.upenn.edu/book/customer-centricity/?utm_source=Coursera&utm_medium=Web&utm_campaign=custcent" SuggBy="Pete Fader�s" />
<Suggestion Text="Global Brand Power" Link="http://wdp.wharton.upenn.edu/books/global-brand-power/?utm_source=Coursera&utm_medium=Web&utm_campaign=glbrpower" SuggBy="Barbara Kahn�s" />
</SuggestedReadings>
Code Is:
string str = CD.SRList.Replace("&", "&");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(str);
XmlNode SuggestionListNode = xmlDoc.SelectSingleNode("/SuggestedReadings/Suggestion");
foreach (XmlNode node in SuggestionListNode)
{
COURSESUGGESTEDREADING CSR = new COURSESUGGESTEDREADING();
var s = db.COURSESUGGESTEDREADINGS.OrderByDescending(o => o.SRID);
CSR.SRID = (s == null ? 0 : s.FirstOrDefault().SRID) + 1;
CSR.COURSEID = LibId;
CSR.TEXT = node.Attributes.GetNamedItem("Text").Value;
CSR.LINK = node.Attributes.GetNamedItem("Link").Value; ;
CSR.SUGBY = node.Attributes.GetNamedItem("SuggBy").Value; ;
CSR.ACTIVEFLAG = "Y";
CSR.CREATEDBY = CD.CreatedBy;
CSR.CREATEDDATE = DateTime.Now;
db.COURSESUGGESTEDREADINGS.Add(CSR);
}
You should use SelectNodes, not SelectSingleNode, since you are trying to get multiple rows out of the XML document.
Use this:
XmlNodeList SuggestionListNode = xmlDoc.SelectNodes("//Suggestion");
foreach (XmlNode node in SuggestionListNode)
{
}
You can try this.
XDocument xdoc = XDocument.Load("data.xml");
var xmlData = from lv1 in xdoc.Descendants("Suggestion")
select new {
Text = lv1.Attribute("Text").Value,
Link = lv1.Attribute("Link").Value,
SuggBy = lv1.Attribute("SuggBy").Value
};
foreach (var item in xmlData){
// your logic here
}
How would I go about getting the ID information using Linq. I'm trying to add them to an array of int.
<FactionAttributes>
<name>Player</name>
<id>0</id>
<relationModifier>1</relationModifier>
<relations>
<id0>100</id0>
<id1>50</id1>
<id2>50</id2>
<id3>50</id3>
<id4>50</id4>
<id5>50</id5>
</relations>
</FactionAttributes>
That is my XML.
Here is the code I'm using so far.
void InitFactions()
{
int count = 0;
string filepath = Application.dataPath + "/Resources/factiondata.xml";
XDocument factionXML = XDocument.Load(filepath);
var factionNames = from factionName in factionXML.Root.Elements("FactionAttributes")
select new {
factionName_XML = (string)factionName.Element("name"),
factionID_XML = (int)factionName.Element("id"),
factionRelations_XML = factionName.Element("relations")// Need to turn this into array.
};
foreach ( var factionName in factionNames)
++count;
foreach ( var factionName in factionNames)
{
Factions f = new Factions();
f.otherFactionsName = new string[count];
f.otherFactionsRelation = new int[count];
int others = 0;
f.FactionName = factionName.factionName_XML;
Debug.Log(factionName.factionRelations_XML);
// Adds Rivals, not self to other list.
foreach (var factionName2 in factionNames)
{
if (factionName.factionID_XML == factionName2.factionID_XML)
continue;
f.otherFactionsName[(int)factionName2.factionID_XML] = factionName2.factionName_XML;
// THIS IS WHERE IM ADDING THE RELATIONS IN //
f.otherFactionsRelation[(int)factionName2.factionID_XML] = factionName.factionRelations_XML[(int)factionName2.factionID_XML];
Debug.Log(f.FactionName + " adds: " + factionName2.factionName_XML);
++others;
}
}
}
I have made multiple attempts using nodes and what not. I can't seem to figure out the correct syntax.
XDocument doc = XDocument.Load(Path);
//To get <id>
var MyIds = doc.Element("FactionAttributes").Element("id").Value;
//To get <id0>, <id1>, etc.
var result = doc.Element("FactionAttributes")
.Element("relations")
.Elements()
.Where(E => E.Name.ToString().Contains("id"))
.Select(E => new { IdName = E.Name, Value = E.Value});
If you want array of ints replace the select with this
.Select(E => Convert.ToInt32(E.Value)).ToArray();
If you are just after the relations Ids use this simple query
var doc = XDocument.Load("c:\\tmp\\test.xml");
var ids = doc.Descendants("relations").Elements().Select(x => x.Value);
If you want the Id and the relations ids in one array use this
var id = doc.Descendants("id").Select(x=>x.Value).Concat(doc.Descendants("relations").Elements().Select(x => x.Value));
I have XElement object formated like this :
<Setting guid="3bcedf55-b75f-456b-b90a-a92cbbb022ga">
<PatientFieldList>
<PatientFieldSetting PatientName="UserDecision" PatentFieldLength="64" />
<PatientFieldSetting PatientName="prohibited" PatentFieldLength="128" />
</PatientFieldList>
</Setting>
I have to get values of all attributes in all nodes but I don't know how :/ I tried
xml.Elements("PatientFieldList")
xml.Descendants("PatientsSettingsFieldsList").Where(x => x.Attribute("PatentFieldLength").Value == 64)`
I have a lot of node like that so i wonder if there is easy way to access to these attribute by '[]' or somehow.
Code:
using System;
using System.Linq;
using System.Xml.Linq
var xml = "<Setting ...";
var doc = XElement.Parse(xml);
int i; // for int parse
var q = from node in doc.Descendants("PatientFieldSetting")
let name = node.Attribute("PatientName")
let length = node.Attribute("PatentFieldLength")
select new { Name = (name != null) ? name.Value : "", Length = (length != null && Int32.TryParse(length.Value, out i)) ? i : 0 };
foreach (var node in q)
{
Console.WriteLine("Name={0}, Length={1}", node.Name, node.Length);
}
Output:
Name=UserDecision, Length=64
Name=prohibited, Length=128
This will print out attributes of all nodes which have attributes in your xml:
XDocument doc = //your data
var q = from node in doc.Descendants()
where node.Attributes().Count() > 0
select new {NodeName = node.Name, Attributes = node.Attributes()};
foreach (var node in q)
{
Console.WriteLine( node.NodeName );
foreach (var attribute in node.Attributes)
{
Console.WriteLine(attribute.Name + ":" + attribute.Value);
}
Console.WriteLine();
}
If you only want PatientFieldSetting nodes filter for the name:
from node in doc.Descendants("PatientFieldSetting")