组合或联接从单独 XML 文件中读取的对象的最有效方法
本文关键字:读取 对象 方法 有效 文件 XML 单独 组合 | 更新日期: 2023-09-27 18:35:28
我每天早上都会在单独的XML文件中接收大量数据。我需要合并 XML 中的对象并从中生成报告。我希望为这个问题使用最佳解决方案。
为了证明,我虚构了以下示例:
有 2 个 XML 文件:
第一个是语言列表及其使用的国家/地区。第二个是产品列表及其销售国家/地区。我生成的报告是产品名称,后跟包装必须使用的语言。
XML1:
<?xml version="1.0" encoding="utf-8"?>
<languages>
<language>
<name>English</name>
<country>8</country>
<country>9</country>
<country>3</country>
<country>11</country>
<country>12</country>
</language>
<language>
<name>French</name>
<country>3</country>
<country>6</country>
<country>7</country>
<country>13</country>
</language>
<language>
<name>Spanish</name>
<country>1</country>
<country>2</country>
<country>3</country>
</language>
</languages>
XML2:
<?xml version="1.0" encoding="utf-8"?>
<products>
<product>
<name>Screws</name>
<country>3</country>
<country>12</country>
<country>29</country>
</product>
<product>
<name>Hammers</name>
<country>1</country>
<country>13</country>
</product>
<product>
<name>Ladders</name>
<country>12</country>
<country>39</country>
<country>56</country>
</product>
<product>
<name>Wrenches</name>
<country>8</country>
<country>13</country>
<country>456</country>
</product>
<product>
<name>Levels</name>
<country>19</country>
<country>18</country>
<country>17</country>
</product>
</products>
示例程序输出:
Screws -> English, French, Spanish
Wrenches -> English, French
Hammer - > French, Spanish
Ladders-> English
目前,我反序列化为数据集,然后使用 linq 跨数据集联接以生成所需的报表字符串。(如下所示 - 将文件名作为命令行参数传递)。
public static List<String> XMLCombine(String[] args)
{
var output = new List<String>();
var dataSets = new List<DataSet>();
//Load each of the Documents specified in the args
foreach (var s in args)
{
var path = Environment.CurrentDirectory + "''" + s;
var tempDS = new DataSet();
try
{
tempDS.ReadXml(path);
}
catch (Exception ex)
{
//Custom Logging + Error Reporting
return null;
}
dataSets.Add(tempDS);
}
//determine order of files submitted
var productIndex = dataSets[0].DataSetName == "products" ? 0:1;
var languageIndex = dataSets[0].DataSetName == "products" ? 1:0;
var joined = from tProducts in dataSets[productIndex].Tables["product"].AsEnumerable()
join tProductCountries in dataSets[productIndex].Tables["country"].AsEnumerable() on (int)tProducts["product_id"] equals (int)tProductCountries["product_id"]
join tLanguageCountries in dataSets[languageIndex].Tables["country"].AsEnumerable() on (String)tProductCountries["country_text"] equals (String)tLanguageCountries["country_text"]
join tLanguages in dataSets[languageIndex].Tables["language"].AsEnumerable() on (int)tLanguageCountries["language_Id"] equals (int)tLanguages["language_Id"]
select new
{
Language = tLanguages["name"].ToString(),
Product = tProducts["name"].ToString()
};
var listOfProducts = joined.OrderByDescending(_ => _.Product).Select(_ => _.Product).Distinct().ToList();
foreach (var e in listOfProducts)
{
var e1 = e;
var languages = joined.Where(_ => _.Product == e1).Select(_ => _.Language).Distinct().ToList();
languages.Sort();
//Custom simple Array to text method
output.Add(String.Format("{0} {1}", e, ArrayToText(languages)));
}
return output;
}
这工作正常,但我知道这个问题必须有更优化的解决方案(特别是当 XML 文件在现实生活中很大时)。是否有人在替代方法(linq 除外)方面有经验或优化当前方法的建议,这将使我更接近最佳解决方案?
提前非常感谢。
溶液实施建议的解决方案:Casperah使用字典的方法在312ms内处理数据集。Yamen的方法使用Linq Lookup在452ms内处理数据集。
您有两个问题,内存使用率和 CPU 使用率。
若要限制内存使用量,可以使用 XmlReader,它只读取大型 xml 文件中的一小部分。要限制 CPU 使用率,您应该在国家/地区代码上有一个索引。
我会这样做:1.阅读所有语言并将其插入字典中,如下所示: 键是国家/地区,值是语言列表。 词典>国家=新词典>();2. 使用 XmlReader 一次读取一个产品3.查找国家/地区并写出语言,可能使用哈希集以避免重复的语言。
那将是我的选择-祝你好运
我创建了此示例:
Dictionary<int, List<string>> countries = new Dictionary<int, List<string>>();
XmlReader xml = XmlReader.Create("file://D:/Development/Test/StackOverflowQuestion/StackOverflowQuestion/Countries.xml");
string language = null;
string elementName = null;
while (xml.Read())
{
switch (xml.NodeType)
{
case XmlNodeType.Element:
elementName = xml.Name;
break;
case XmlNodeType.Text:
if (elementName == "name") language = xml.Value;
if (elementName == "country")
{
int country;
if (int.TryParse(xml.Value, out country))
{
List<string> languages;
if (!countries.TryGetValue(country, out languages))
{
languages = new List<string>();
countries.Add(country, languages);
}
languages.Add(language);
}
}
break;
}
}
using (StreamWriter result = new StreamWriter(@"D:'Development'Test'StackOverflowQuestion'StackOverflowQuestion'Output.txt"))
{
xml = XmlReader.Create("file://D:/Development/Test/StackOverflowQuestion/StackOverflowQuestion/Products.xml");
string product = null;
elementName = null;
HashSet<string> languages = new HashSet<string>();
while (xml.Read())
{
switch (xml.NodeType)
{
case XmlNodeType.Element:
elementName = xml.Name;
break;
case XmlNodeType.Text:
if (elementName == "name")
{
if (product != null && languages != null)
{
result.Write(product);
result.Write(" -> ");
result.WriteLine(string.Join(", ", languages.ToArray()));
languages.Clear();
}
product = xml.Value;
}
if (elementName == "country")
{
int country;
if (int.TryParse(xml.Value, out country))
{
List<string> countryLanguages;
if (countries.TryGetValue(country, out countryLanguages))
foreach (string countryLanguage in countryLanguages) languages.Add(countryLanguage);
}
}
break;
}
}
}
}
它生成此示例:
Screws -> English, French, Spanish
Hammers -> Spanish, French
Ladders -> English
Wrenches -> English, French
XmlReader.Create需要一个URI,你也可以使用类似的东西:"http://www.mysite.com/countries.xml"
好吧,这仍然是 LINQ 到 XML,但我认为就算法而言,它非常有效。唯一的问题是你的XML非常大(即大于RAM可以容纳)。否则,它不会比这快得多。
假设languageFile
和productFile
包含相关的 XML 文件。
将语言转换为查找:
var languages = (from language in XElement.Load(languageFile).Descendants("language")
from country in language.Elements("country")
select new {Language = language.Element("name").Value, Country = country.Value})
.ToLookup(l => l.Country, l => l.Language);
然后通过语言查找获取产品:
var products = from product in XElement.Load(productFile).Descendants("product")
select new {Product = product.Element("name").Value,
Languages = product.Elements("country").SelectMany(e => languages[e.Value]).Distinct().ToList()};
当然,您也可以打印出来:
foreach (var product in products.Where(x => x.Languages.Count > 0))
{
Console.WriteLine("{0} -> {1}", product.Product, String.Join(", ", product.Languages));
}
其中返回:
Screws -> English, French, Spanish
Hammers -> Spanish, French
Ladders -> English
Wrenches -> English, French
在您的情况下,我会将语言文件中的数据存储到字典或类似的东西中,然后我会解析每个产品文件并动态生成最终的组合结果。我想这种方法会更快,您可以避免大量数据出现的内存问题。