public string GetLogName(string config)
{
XDocument xDoc = XDocument.Load(config);
XElement[] elements = xDoc.Descendants("listeners").Descendants("add").ToArray();
foreach (var element in elements)
{
if (element.Attribute("fileName").Value != null)
{
string filename = element.Attribute("fileName").Value;
int location = filename.IndexOf("%");
Console.WriteLine("string to return: " + filename.Substring(0, location));
return filename.Substring(0, location);
}
}
}
I am trying to retrieve the "fileName" attribute from each element in the elements array, but there are some cases when the "fileName" attribute does not exist and fails with the following error: NullReferenceException was unhandled. Object reference not set to an instance of an object.
In my case there are two "add" nodes that do not have a "fileName" attribute, but the third add node has it.
How can I skip over entries that do not have a "fileName" attribute, or can you recommend a better way to retrieve this attribute?
One way is to filter out the list before you process it:
XElement[] elements = xDoc.Descendants("listeners")
.Descendants("add")
.Where (d => d.Attribute("filename") != null )
.ToArray();
--- IMHO this is how I would rewrite the method, using linq and regex ---
var elements =
XDocument.Load(config);
.Descendants("listeners")
.Descendants("add")
.Where (node => node.Attribute("filename") != null )
.ToList();
return elements.Any() ? elements.Select (node => node.Attribute("filename").Value )
.Select (attrValue => Regex.Match(attrValue, "([^%]+)").Groups[1].Value)
.First ()
: string.Empty;
You should be able to do this simply by changing this line:
if (element.Attribute("fileName").Value != null)
To:
if (element.Attribute("fileName") != null)
change your if statement to this :
if (element.Attribute("fileName") != null)
Related
I have a page type with a ContentArea as one of its properties
(currentPage.PrimaryComponentArea)
How can I get the block items stored in this property based on its type.
I also want to be to access properties on the block so I need to convert it from ContentAreaItem to the actuall block type.
public ActionResult Index(RatePlanPageType currentPage)
{
..........
var allItems = currentPage.PrimaryComponentArea.Items;
var blocks = allItems.Where(x => bla bla bla < Can I do it using linq
}
this is my first Episerver project so I hope this is not a stupid question.
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
if (page != null && page.ContentArea != null && page.PrimaryComponentArea.Items.Any())
{
foreach (var contentItem in page.PrimaryComponentArea.Items)
{
var item = contentLoader.Get<YOUR_CONTENT_TYPE>(contentItem.ContentLink);
// do your stuff
}
}
Items in ContentArea are saved as ContentReference, and all ContentReference reference to an IContent. A block and page type is an IContent.
If you want to restrict contentareas to one type: [AllowedTypes(new [] {typeof(PageData), typeof(BlockData)})]
https://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Content/Properties/Property-types/Restricting-content-types-in-properties/
var resultList = new List<IContent>();
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
foreach (ContentAreaItem contentAreaItem in currentPage.PrimaryComponentArea.FilteredItems)
{
if (contentAreaItem != null && contentAreaItem.ContentLink != null &&
contentAreaItem.ContentLink != ContentReference.EmptyReference)
{
IContent content;
if (contentLoader.TryGet(contentAreaItem.ContentLink, out content))
if (content != null)
{
resultList.Add(content);
}
}
}
Above code will give you a list of blocks in the contentarea as IContent. Note that I used FilteredItems which also takes into consideration any personlization, publish status etc.
To access the properties of the blocks you will need to cast them to their type.
So something like this might point you in the right direction
foreach (IContent content in resultList)
{
var block = content as YourBlockType;
if (content != null)
{
// the content is of type YourBlockType. Do work
}
}
To solve this using linq
// ServiceLocator as example, use dependency injection!
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
// Get all CodeBlock's from the contentarea
IEnumerable<CodeBlock> items = currentPage.ContentArea?.Items?
.Where(block => block.GetContent() is CodeBlock) // validate type
.Select(block => contentLoader.Get<CodeBlock>(block.ContentLink));
// Run a where on a specific property on the blocks
IEnumerable<CodeBlock> items = currentPage.ContentArea?.Items?
.Where(block => block.GetContent() is CodeBlock) // validate type
.Select(block => contentLoader.Get<CodeBlock>(block.ContentLink))
.Where(block => block.Tags.Contains("Episerver"));
Now the trick here is to use the .Where(block => block.GetContent() is CodeBlock), the block.GetContent() will resolve an IContent object which allows you to verify the type of the block
For a more generic approach use this
IEnumerable<IContent> items = currentPage.ContentArea?.Items?
.Select(content => contentLoader.Get<IContent>(content.ContentLink)) // Get as IContent
.Where(content => content.Name.Contains("block")); // any properties you like
The last version will also include pages if they are dropped in the contentarea, if you only like to support a specific type use the same type validation
IEnumerable<IContent> items = currentPage.ContentArea?.Items?
.Where(content => content.GetContent() is BlockData) // validate type
.Select(content => contentLoader.Get<IContent>(content.ContentLink))
.Where(content => content.Name.Contains("block"));
Always check variables if they are null when using null propagation as I do ContentArea?.Items?...
if(items != null) {
// Yay!
}
I'm new to Roslyn. I'm wondering if there's a way to tell if a variable is in scope at some position in a semantic model. To give a bit of background on what I'm doing, I'm trying to transform foreach blocks that iterate through the results of a Select, e.g. of the form
foreach (string str in new int[0].Select(i => i.ToString()))
{
}
to
foreach (int item in new int[0])
{
string str = item.ToString();
}
Here is the relevant portion of my code fix provider. Currently, I am hard-coding the iteration variable to be item:
var ident = SyntaxFactory.Identifier("item");
Then, I am retrieving the Body of the selector's SimpleLambdaExpressionSyntax, and (in the above case) substituting the parameter i with item to get item.ToString():
var paramTokens = from token in selectorBody.DescendantTokens()
where token.Text == selectorParam.Identifier.Text
select token;
selectorBody = selectorBody.ReplaceTokens(paramTokens, (_, __) => ident);
I want to know if there is a way to tell whether a variable named item is already in scope at the location of the foreach block, so my code fix provider does not generate a conflicting variable declaration. Would this be possible to somehow achieve using the SemanticModel/Symbol/etc. APIs?
Thanks.
I could think of two ways to do it.
Using this test code so I could test the different declarations (field, property, variable, class names)
const string code = #"
public class AClass{
private int MyFld = 5;
protected double MyProp{get;set;}
public void AMethod(){
string myVar = null;
for (int myIterator=0; myIterator<10;myIterator++)
foreach (string str in new int[0].Select(i => i.ToString())){ }
}
public void AnotherMethod()
{
string anotherVar = null;
}
}";
-
void Main()
{
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();
var startNode = root
.DescendantNodes()
.OfType<SimpleLambdaExpressionSyntax>() // start at the Select() lambda
.FirstOrDefault();
FindSymbolDeclarationsInAncestors(startNode, "myVar").Dump(); // True
FindSymbolDeclarationsInAncestors(startNode, "anotherVar").Dump(); // False
CompilationLookUpSymbols(tree, startNode, "myVar").Dump(); // True
CompilationLookUpSymbols(tree, startNode, "anotherVar").Dump(); // False
}
// You could manually traverse the ancestor nodes, and find the different DeclarationSyntax-es.
// I may have missed some, like CatchDeclarationSyntax..
// Error-prone but more fun.
public bool FindSymbolDeclarationsInAncestors(CSharpSyntaxNode currentNode, string symbolToFind)
{
return currentNode
.Ancestors().SelectMany(a => a.ChildNodes()) // get direct siblings
.SelectMany(node => // find different declarations
(node as VariableDeclarationSyntax)?.Variables.Select(v => v.Identifier.ValueText)
?? (node as FieldDeclarationSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText)
?? (node as LocalDeclarationStatementSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText)
?? new[] {
(node as PropertyDeclarationSyntax)?.Identifier.ValueText,
(node as MethodDeclarationSyntax)?.Identifier.ValueText,
(node as ClassDeclarationSyntax)?.Identifier.ValueText,
})
.Any(member => string.Equals(member, symbolToFind));
}
// Or use the SemanticModel from the CSharpCompilation.
// Possibly slower? Also, not as much fun as manually traversing trees.
public bool CompilationLookUpSymbols(SyntaxTree tree, CSharpSyntaxNode currentNode, string symbolToFind)
{
var compilation = CSharpCompilation.Create("dummy", new[] { tree });
var model = compilation.GetSemanticModel(tree);
return model.LookupSymbols(currentNode.SpanStart, name: symbolToFind).Any();
}
Im trying to read a certain attributes from following xml file (as console program)
http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml
As you see inside 'forecast' element there are multiple 'time' elements. What I want is to pick certain 'time' element and then pick given thing inside of it (lets say 'symbol') and print all/any attributes it has.
I want to be able to control which 'time' element I pick and which attributes I want to print.
This far all I have managed to do is to print every 'time' element and their attributes and also I managed to print every attribute inside of given 'time' element. But I just can't figure how to control it.
With the following code, I can print everything inside the first 'time' element. Item(0) is the index of element and the for loop makes sure that I don't get empty lines. As you can see from xml file, some 'time' elements has different amount of attributes inside of them so I guess I need to call them By name insted of index.
static void xmlReader()
{
int i;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(parseLink());
foreach (XmlNode xmlNode in xmlDoc.DocumentElement.GetElementsByTagName("time").Item(0))
for (i = 0; i < xmlNode.Attributes.Count; i++)
{
Console.WriteLine(xmlNode.Attributes[i].Value);
}
Console.ReadKey();
}
Use Linq2Xml, it's much easier and convenient.
public static void Main()
{
var forecast = XDocument.Load(#"http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml")
.Root
.Element("forecast");
foreach (var time in forecast.Elements("time")
.Where(e => e.Element("clouds")
.Attribute("value")
.Value == "overcast clouds"))
{
Console.WriteLine(time.Element("symbol").Attribute("name").Value);
}
}
Using XDocument is a bit easier here...
private static void XmlOutput()
{
var filePathAndName = #"http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml";
var xmlDoc = XDocument.Load(filePathAndName);
// Get list of Nodes matching the "time" name or any other filters you wish.
var nodes = xmlDoc.Descendants().Where(nd => nd.Name.LocalName == "time");
// Filter the node list to only those where the date is as specified (and any other filters you wish).
// This could be done in the initial linq query - just making it clearer here.
nodes = nodes.Where(nd => nd.Attributes().Any(cnd => cnd.Name.LocalName == "from" && cnd.Value.Contains("2015-03-07")));
foreach (XElement element in nodes)
{
// For each descendant node where named "symbol"...
foreach(var node in element.Descendants().Where(nd => nd.Name.LocalName == "symbol"))
{
// Write out these particular attributes ("number" and "name")
string output = "";
output += node.Attributes().FirstOrDefault(nd => nd.Name.LocalName == "number").Value;
output += ", " + node.Attributes().FirstOrDefault(nd => nd.Name.LocalName == "name").Value;
Console.WriteLine(output);
}
}
Console.ReadKey();
}
Similar #aush's response, but with some formatting
var doc = XDocument.Load(#"http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml");
var forecastEl = doc.Root.Element(XName.Get("forecast"));
var timeNodes = from e in forecastEl.Elements("time")
where e.Element("symbol")
.Attribute(XName.Get("name"))
.Value == "light snow"
select e;
var colFormat = "{0,-20} {1,-20} {2,-30}";
Console.WriteLine(colFormat, "TimeFrom", "TimeTo", "SymbolName");
foreach(var time in timeNodes)
Console.WriteLine(colFormat
, time.Attribute("from").Value
, time.Attribute("to").Value
, time.Element("symbol").Attribute("name").Value);
Results:
TimeFrom TimeTo SymbolName
2015-03-07T12:00:00 2015-03-07T15:00:00 light snow
2015-03-07T15:00:00 2015-03-07T18:00:00 light snow
You can use XmlReader (tutorial) as flowing,
Here I get from attribute from time element and name attribute sybol element.. And they are for same Time element. Also added extracting example for value of cloud
using (XmlReader reader = XmlReader.Create(#"http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml"))
{
// Walk through all Elements
while (reader.Read())
{
// If we meet time element ; go inside and walk
if (reader.Name == "time")
{
Console.WriteLine("A new TIME ");
// Extract from attribute
String from = reader["from"];
if (from != null)
{
Console.WriteLine("\t From : " + from);
}
// Now walk through all elements inside same Time element
// Here I use do-while ; what we check is End element of time : </time> .. when we walk till we meet </time> we are inside children of same Time
// That mean we start from <time> and walk till we meet </time>
do
{
reader.Read();
if (reader.Name == "symbol")
{
// You can use this approach for any Attribute in symbol Element
String name = reader["name"];
if (name != null)
{
Console.WriteLine("\t Symbol name :" + name);
}
}
if (reader.Name == "clouds")
{
String clouds = reader["value"];
if (clouds != null)
{
Console.WriteLine("\t\t Clouds value : " + clouds);
}
}
} while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "time");
}
}
}
Simply try this on your Console program..
Using my xml library here, you can search for an actual DateTime value like this:
XElement root = XElement.Load(#"http://api.openweathermap.org/data/2.5/forecast?q=lahti,fin&mode=xml");
var search = DateTime.Parse("2015-03-07T21:00:00");
XElement time = root.XPathElement("//time[#from >= {0} and #to > {1}]", search, search);
var value = time.ToString();
I am filling a list from an XML file. Some nodes may not exist and this causes an exception because it returns null. Here is the code:
public static List<Compte> getXmlComptes(string pathXml)
{
var doc = XDocument.Load(pathXml);
var comptes = doc.Descendants("Anzeige").Descendants("Kunde").Descendants("Konto").Select(p => new Compte()
{
NumCompte = p.Element("KtoNr") != null ? p.Element("KtoNr").Value : String.Empty,
typeCompte = p.Element("KontoArt") != null ? p.Element("KontoArt").Value : String.Empty,
Trans = getXmlTransactions(pathXml)
}).ToList();
return comptes;
}
How can I make a controf before adding items to the list. Thank you.
exemple of the xml file :
<Anzeige>
<Kunde>
<IdClient>ppp</IdClient>
<Konto>
<NumCompte>258</NumCompte>
<Transaction>
<idTrans>85555</idTrans>
<type>blebleble</type>
</Transaction>
<Transaction>
<idTrans>85555</idTrans>
<type>blebleble</type>
</Transaction>
</Konto>
</Kunde>
</Anzeige>
code of getXmlTransaction :
public static List<Transaction> getXmlTransactions(string pathXml)
{
var doc = XDocument.Load(pathXml);
var transactions = doc.Descendants("Anzeige").Descendants("Kunde").Descendants("Konto").Descendants("Transaktion").Select(p => new Transaction()
{
TransID = p.Element("TransID") != null ? p.Element("TransID").Value : String.Empty,
TypeTransaction = p.Element("TransArt") != null ? p.Element("TransArt").Value : String.Empty
}).ToList();
if (transactions != null)
return transactions.ToList();
else
return new List<Transaction>();
}
Use casting of element to string instead of reading it's value directly. If element was not found, you will have null string instead of exception:
var doc = XDocument.Load(pathXml);
var comptes = doc.Descendants("Anzeige")
.Descendants("Kunde")
.Descendants("Konto")
.Select(k => new Compte {
NumCompte = (string)k.Element("KtoNr"),
typeCompte = (string)k.Element("KontoArt"),
Trans = getXmlTransactions(k)
}).ToList();
If you want empty string instead of null when element not found, you can use null-coalescing operator
NumCompte = (string)p.Element("KtoNr") ?? ""
Use same technique for parsing nodes which may not exist. And I'm pretty sure that it's getXmlTransactions(pathXml) method throws exception.
UPDATE: Do not load whole xml document when you are getting transactions. How would you know which Konto element transactions to read. Pass Konto element instead and get its transactions:
public static List<Transaction> getXmlTransactions(XElement konto)
{
return konto.Elements("Transaction")
.Select(t => new Transaction {
TransID = (string)t.Element("idTrans"),
TypeTransaction = (string)t.Element("type")
}).ToList();
}
NOTE: You have <idTrans> (instead TransID) and <type> (instead TransArt) elements in <Transaction> (instead Transaktion)! Also there is no KtoNr and KontoArt elements in your xml. Read element names carefully. Also instead of looking for all descendants it's better to search in direct children:
doc.Root.Elements("Kunde").Elements("Konto") ...
I don't know why I'm having so much trouble with this, but I'm hoping someone can get me pointed in the right direction.
I have these few lines of code :
var xDoc = new XmlDocument();
xDoc.LoadXml(xelementVar.ToString());
if (xDoc.ChildNodes[0].HasChildNodes)
{
for (int i = 0; i < xDoc.ChildNodes[0].ChildNodes.Count; i++)
{
var sFormatId = xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"].Value;
// Do some stuff
}
// Do some more stuff
}
The problem is that the xDoc I'm getting doesn't always have the formatID node, so I end up getting a null reference exception, although 99% of the time it works perfectly fine.
My question :
How can I check if the formatID node exists before I try to read the Value out of it?
if a node does not exist, it returns null.
if (xDoc.ChildNodes[0].ChildNode[i].Attributes["formatID"] != null)
sFormatId = xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"].Value;
of you can do it a shortcut way
var sFormatId = xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"] != null ? xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"].Value : "formatID not exist";
The format is like this.
var variable = condition ? A : B;
this is basically saying that if the condition is true, then variable = A, otherwise, variable = B.
Could you use DefaultIfEmpty()?
E.g
var sFormatId = xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"]
.Value.DefaultIfEmpty("not found").Single();
Or as others have suggested, check that the attribute is not null:
if (xDoc.ChildNodes[0].ChildNodes[i].Attributes["formatID"] != null)
You can also do this:
if (xDoc.ChildNodes[0].HasChildNodes)
{
foreach (XmlNode item in xDoc.ChildNodes[0].ChildNodes)
{
string sFormatId;
if(item.Attributes["formatID"] != null)
sFormatId = item.Attributes["formatID"].Value;
// Do some stuff
}
}
you can check that like this
if(null != xDoc.ChildNodes[0].ChildNode[i].Attributes["formatID"])
I think a cleaner way to do this would be:
var xDoc = new XmlDocument();
xDoc.LoadXml(xelementVar.ToString());
foreach(XmlNode formatId in xDoc.SelectNodes("/*/*/#formatID"))
{
string formatIdVal = formatId.Value; // guaranteed to be non-null
// do stuff with formatIdVal
}
In most case we face issues because an XPath does not exists, It returns null and our code breaks because of the InnerText.
You can only check XPath exists or not and it returns null when does not exist.
if(XMLDoc.SelectSingleNode("XPath") <> null)
ErrorCode = XMLDoc.SelectSingleNode("XPath").InnerText