如何处理缺少元素时失败的LINQ表达式
本文关键字:元素 失败 表达式 LINQ 何处理 处理 | 更新日期: 2023-09-27 18:06:45
我是一个使用LINQ to XML的新手,我已经得到了这个工作的代码(大多数时候):
private long processFile(StreamWriter oWriter, string inFileName)
{
XDocument xmlDoc = XDocument.Load(inFileName);
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
Folder = d.Element("FOLDER").Attribute("name").Value
,
File = d.Element("FILE").Attribute("filename").Value
,
Comment = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Comment(idmComment)")
.First()
.Attribute("value").Value
,
Title = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Title(idmName)")
.First()
.Attribute("value").Value
,
DocClass = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Document Class(idmDocType)")
.First()
.Attribute("value").Value
}
).ToList<DocMetaData>();
OutputListToFile(oWriter, docList);
return docList.LongCount();
}
这在第117行(select表达式)失败:
System.NullReferenceException: Object reference not set to an instance of an object.
at CBMI.WinFormsUI.GridForm.<processFile>b__3(XElement d) in C:'ProjectsVS2010'CBMI.LatitudePostConverter'CBMI.LatitudePostConverter'CBMI.WinFormsUI'GridForm.cs:line 117
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at CBMI.WinFormsUI.GridForm.processFile(StreamWriter oWriter, String inFileName) in C:'ProjectsVS2010'CBMI.LatitudePostConverter'CBMI.LatitudePostConverter'CBMI.WinFormsUI'GridForm.cs:line 115
at CBMI.WinFormsUI.GridForm.btnProcess_Click(Object sender, EventArgs e) in C:'ProjectsVS2010'CBMI.LatitudePostConverter'CBMI.LatitudePostConverter'CBMI.WinFormsUI'GridForm.cs:line 85
数据是格式良好的XML。在这个给定的XML文件中有许多<DOCUMENT>
节点,大多数(但不是全部)<DOCUMENT>
节点包含一个<FOLDER>
节点。我发现这是通过暴力打开XML文件与VStudio 2010,并使用查找命令给出匹配行的计数。
是否有一种方法可以改善LINQ,使其在数据不完美时不会失败?并且,是否有一种方法可以看到LINQ表达式的哪个部分实际上失败了(我猜这是由于缺少<FOLDER>
节点,但这可能是错误的,并且是一种丑陋的暴力方法来排除故障)。
这里有一个<DOCUMENT>
确实包含适当的<FOLDER>
节点(在最底部):
<?xml version="1.0" ?>
<DOCUMENTCOLLECTION>
<DOCUMENT>
<FILE filename="P:'LatitudeConsulting'LatConConverter-1.8.2'ConverterOutput'B0000002'3rd Party CON'D003694452.0001.tif"
outputpath="P:'LatitudeConsulting'LatConConverter-1.8.2'ConverterOutput'B0000002'3rd Party CON"/>
<ANNOTATION filename=""/>
<INDEX name="Access Level(idmAccessLevel)" value="Admin"/>
<INDEX name="Added By Group(idmAddedByGroup)" value="General Users"/>
<INDEX name="Added By User(idmDocOwner)" value="Import"/>
<INDEX name="Allow Secondary Version Lines?(idmDocVariants)" value="Yes"/>
<INDEX name="Application(idmVerApplication)" value=""/>
<INDEX name="Archive Category(idmDocDispCategory)" value="Archive"/>
<INDEX name="Archive Date(idmVerDispDate)" value=""/>
<INDEX name="Archive Repository(idmVerDispId)" value=""/>
<INDEX name="ArchivedDocument" value="NO"/>
<INDEX name="Availability Status(idmVerAvailStat)" value="Online"/>
<INDEX name="CAN(idmDocCustom4)" value=""/>
<INDEX name="Checked In By Group(idmVerCheckinGroup)" value="General Users"/>
<INDEX name="Checked In By User(idmVerCheckinUser)" value="Import"/>
<INDEX name="Checked Out?(idmVerCheckoutPending)" value="No"/>
<INDEX name="Checkin Date(idmVerCreateDate)" value="3/9/2001 9:20:38 AM"/>
<INDEX name="Child Count(idmVerCD)" value="0"/>
<INDEX name="Comment(idmComment)" value="1983'06_June_Meeting"/>
<INDEX name="Comment(idmVerComment)" value=""/>
<INDEX name="Content Search Repository(idmVerCsiId)" value=""/>
<INDEX name="Current Content Srch Repository(idmDocCurVerCsiId)" value=""/>
<INDEX name="Current Version Author(idmAddedByUser)" value="Import"/>
<INDEX name="Current Version Checked Out?(idmDocCurVerCheckedOut)" value="No"/>
<INDEX name="Current Version Date(idmDocCurVerDate)" value="3/9/2001 9:20:38 AM"/>
<INDEX name="Current Version ID(idmDocCurVerNum)" value="1"/>
<INDEX name="Current Version Index ID(idmDocCurVerCsiCid)" value=""/>
<INDEX name="Date Added(idmDateAdded)" value="3/9/2001 9:20:37 AM"/>
<INDEX name="Default Index Versions?(idmDocCsiDefault)" value="No"/>
<INDEX name="DiagnosticID(idmDocCustom5)" value="2-16.MDB-00015"/>
<INDEX name="Document Class(idmDocType)" value="3rd Party CON"/>
<INDEX name="Encrypted File Name(idmVerShelfFileId)" value="_276no__.__1"/>
<INDEX name="ExternalDocument" value="NO"/>
<INDEX name="File Name" value="51099.TIF"/>
<INDEX name="File Name(idmVerFileName)" value="51099.TIF"/>
<INDEX name="File Size(idmVerFileSize)" value="1166770"/>
<INDEX name="Has Annotations?(idmAnnotation)" value=""/>
<INDEX name="Index ID(idmVerCsiCid)" value=""/>
<INDEX name="Indexed Version Limit(idmDocCsiLimit)" value="1"/>
<INDEX name="Indexing Status(idmVerCsiStatus)" value="Not Indexed"/>
<INDEX name="Item ID(idmId)" value="003694452"/>
<INDEX name="Item ID(idmVerDocId)" value="003694452"/>
<INDEX name="Keyword(idmDocKeywords)" value=""/>
<INDEX name="Last Access Date(idmDateAccessed)" value="11/28/2003 3:05:30 PM"/>
<INDEX name="Last Access Date(idmDateModified)" value="8/24/2011 5:52:34 PM"/>
<INDEX name="Last Access Group(idmVerLastGroup)" value="Administrators"/>
<INDEX name="Last Access User(idmModifiedByUser)" value="Admin"/>
<INDEX name="Last Accessed Version(idmDocLastVerId)" value="1"/>
<INDEX name="Latest Version?(idmVerBranchCurVer)" value="Yes"/>
<INDEX name="Merge-Destination Version ID(idmVerMergeDst)" value="0"/>
<INDEX name="Merge-Source Version ID(idmVerMergeSrc)" value="0"/>
<INDEX name="MimeType" value="image/tiff"/>
<INDEX name="Min Item Delete Access Level(idmDocDeleteAccess)" value=""/>
<INDEX name="Modification Date(idmVerFileDate)" value="12/19/2000 11:12:30 AM"/>
<INDEX name="Number of Indexed Versions(idmDocCsiCount)" value="0"/>
<INDEX name="Offline Location(idmVerOfflineLocation)" value=""/>
<INDEX name="Online Disk Space(idmDocOnlineSize)" value="1166770"/>
<INDEX name="Online Limit(idmDocOnlineLimit)" value="5"/>
<INDEX name="Online Version Count(idmDocOnlineCount)" value="1"/>
<INDEX name="Origin ID(idmDocOriginID)" value=""/>
<INDEX name="Origin Library(idmDocOriginLibrary)" value=""/>
<INDEX name="Original File Name(idmDocOriginalFile)" value="51099.TIF"/>
<INDEX name="Permanent Index?(idmVerCsiPermanent)" value="No"/>
<INDEX name="Permanent Version?(idmVerPermanent)" value="No"/>
<INDEX name="Property ID(idmDocDynPropertyId)" value=""/>
<INDEX name="Protected?(idmDocProtected)" value="Yes"/>
<INDEX name="Publishing Status(idmPublish)" value=""/>
<INDEX name="Reclaim Pending?(idmVerReclaimPending)" value=""/>
<INDEX name="Reclaim Submitted Date(idmVerReclaimDate)" value=""/>
<INDEX name="Replica?(idmDocIsReplica)" value="No"/>
<INDEX name="ReplicatedDocument" value="NO"/>
<INDEX name="Secondary Version Line Count(idmVerBranchCount)" value="0"/>
<INDEX name="Source Version Checkout Date(idmVerPrevCheckoutDate)" value=""/>
<INDEX name="Storage Category(idmDocFileCategory)" value="Documents"/>
<INDEX name="Storage Repository(idmVerShelfId)" value="2"/>
<INDEX name="Title(idmName)" value="3rd Party CON Comments"/>
<INDEX name="Version ID(idmVerId)" value="1"/>
<FOLDER name="/NACAIE/1983/06_June_Meeting/NAPNSC"/>
</DOCUMENT>
编辑:解决方案如下(包含LINQ修复这个问题,当文件夹节点可能丢失;正如其他人指出的那样,使用First()可能是危险的做法,但在这种情况下,必须处理丢失的FOLDER节点):
namespace CBMI.Common
{
public static class Extensions
{
public static string SafeGetAttributeValue(this XElement element, string attribute)
{
return (element != null) ?
(element.Attribute(attribute) != null) ?
element.Attribute(attribute).Value : null : null;
}
}
}
private long processFile(StreamWriter oWriter, string inFileName)
{
XDocument xmlDoc = XDocument.Load(inFileName);
List<DocMetaData> docList =
(from d in xmlDoc.Descendants("DOCUMENT")
select new DocMetaData
{
File = d.Element("FILE").Attribute("filename").Value
,
ItemID = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Item ID(idmId)")
.First()
.Attribute("value").Value
,
Comment = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Comment(idmComment)")
.First()
.Attribute("value").Value
,
Title = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Title(idmName)")
.First()
.Attribute("value").Value
,
DocClass = d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Document Class(idmDocType)")
.First()
.Attribute("value").Value
,
Folder = d.Element("FOLDER").SafeGetAttributeValue("name")
}
).ToList<DocMetaData>();
OutputListToFile(oWriter, docList);
return docList.LongCount();
}
你总是可以在选择给定节点之前检查它:
Folder = (d.Element("FOLDER") != null) ? (d.Element("FOLDER").Attribute("name") != null)
? Attribute("name").Value : null
: null
但我承认这可能会有点难看。在这种情况下,可以创建一个XElement扩展方法:
public static class Extensions
{
public static string SafeGetAttributeValue(this XElement element, string attribute)
{
return (element != null) ? (element.Attribute(attribute) != null)
? Attribute(attribute).Value : null
: null
}
}
你可以这样使用:
select new DocMetaData
{
Folder = d.Element("FOLDER").SafeGetAttributeValue("name"),
//the rest of your object creation
}
我不能测试,看看这是否是你确切的问题,但First()
抛出异常,如果在查询中没有任何值。您可以通过使用FirstOrDefault()
来避免这种情况,然后检查结果是否为null
。
我的经验法则是永远不要在查询中间使用First()
, FirstOrDefault()
, Single()
或SingleOrDefault()
方法。它应该始终是最后调用的方法。如果需要从选定项中选择一个属性,请先进行项目,然后调用该方法。这会使代码看起来更整洁,使这种情况更容易处理。
。
// Change this:
d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Comment(idmComment)")
.First()
.Attribute("value").Value
// to this:
d.Elements("INDEX")
.Where(i => i.Attribute("name").Value == "Comment(idmComment)")
.Select(i => i.Attribute("value").Value)
.First()
我将它重写为:
private long processFile(StreamWriter oWriter, string inFileName)
{
XDocument xmlDoc = XDocument.Load(inFileName);
List<DocMetaData> docList = xmlDoc.Descendants("DOCUMENT")
.Select(e => new
{
Folder = (string)e.Element("FOLDER").Attribute("name"),
File = (string)e.Element("FILE").Attribute("filename"),
Comment = (string)e.Elements("INDEX")
.Where(i => (string)i.Attribute("name") == "Comment(idmComment)")
.Select(i => (string)i.Attribute("value"))
.FirstOrDefault(),
Title = (string)e.Elements("INDEX")
.Where(i => (string)i.Attribute("name") == "Title(idmName)")
.Select(i => (string)i.Attribute("value"))
.FirstOrDefault(),
DocClass = (string)e.Elements("INDEX")
.Where(i => (string)i.Attribute("name") == "Document Class(idmDocType)")
.Select(i => (string)i.Attribute("value"))
.FirstOrDefault(),
})
.ToList();
OutputListToFile(oWriter, docList);
return docList.LongCount();
}
在您的linq查询中,对于每个可能为空的字段,如果您为它们赋值,则会抛出异常,您可以:
MyDone = PossibleNullNode != null ? PossibleNullNode.Value : WhateverFloatsYourBoat