XPath not working with SelectSingleNode - c#

private const string TECHACCOUNTAMTITEM_AMT_XPATH = #"//Part[translate(#Type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'instalment']/acord:Jv-Ins-Reinsurance/acord:TechAccount/acord:Subaccount/acord:TechAccountAmtItem[translate(#Type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') != 'ipt' and translate(#AmtStatus, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') != 'informational']/acord:Amt";
var xmlNamespaceManager = this._namespaceManager.GetNamespaceManager(this.TransactionPayload.OuterXml);
var techAccountAmtItemAmt = Decimal.Parse(this.TransactionPayload.SelectSingleNode(TECHACCOUNTAMTITEM_AMT_XPATH, xmlNamespaceManager).InnerText);
The above two statement gives out the value 4500.
But i dont want to use translate and just want to use it directly to fetech the value .
It seems that != is not working in the below and not fetching the value and resulting in null .
Here is the new Xpath i m trying to achieve but not working .
private const string TECHACCOUNTAMTITEM_AMT_XPATH = #"//Part[#Type = 'Instalment']/acord:Jv-Ins-Reinsurance/acord:TechAccount/acord:Subaccount/acord:TechAccountAmtItem[#Type != 'ipt' and #AmtStatus != 'informational']/acord:Amt";
How can I acheive the same ?

The reason for this is that != requires the attribute to exist, while translate will also produce a result if the attribute is missing.
You need to use not() instead:
.../acord:‌​TechAccountAmtItem[not(#‌​Type = 'ipt') and not(#AmtStatus = 'informational')]/...
This assumes that Type could be missing as well. If not, you can use #Type != 'ipt' instead.

Related

XML Attribute Values C#

I am trying to retrieve the value of two XML attributes and having a hard time using Xelement.
Basically I am getting an API response that has uses the same attribute naming convention for two different values.
This is the response I am getting back after making the call.
-<ResponseData>
<Type value="7" id="Level"/>
<Type value="67.80" id="Score"/>
</ResponseData>
I want to set the two values to two different variables. level & score. I have the fulling code however I keep getting back a null reference error.
var xmlResponse = XElement.Parse(response);
var level = "";
var score = "";
if(xmlResponse.Attribute("id").Value == "Level")
{
level = (string) xmlResponse.Attribute("value");
}
if(xmlResponse.Attribute("id").Value == "Score")
{
score = (string) xmlResponse.Attribute("value");
}
I want my two variables to be set to the values I get back from the API call.
Any help is appreciated
Side note, if this is a SOAP service with a WSDL, have you tried letting Visual Studio scaffold it?
So you have to think about it this way.. your now parsed XElement is a ResponseData object, which has a collection of Type objects in it now, and each Type object has a value and id attribute, which means you need to find the right Type entry for each value you're looking for. EG:
string data = #"<ResponseData>
<Type value=""7"" id=""Level""/>
<Type value=""67.80"" id=""Score""/>
</ResponseData>";
var xmlResponse = XElement.Parse(data);
var levelElement = xmlResponse.Elements("Type")?
.FirstOrDefault(x => x.Attribute("id")?.Value == "Level");
var scoreElement = xmlResponse.Elements("Type")?
.FirstOrDefault(x => x.Attribute("id")?.Value == "Score");
int level;
double score;
level = int.Parse(levelElement?.Attribute("value")?.Value ?? "-1");
score = double.Parse(scoreElement.Attribute("value")?.Value ?? "-1");
Console.WriteLine($"Score {score}; Level {level}");
Why not use?
-<ResponseData>
<Type Level="7" Score="67.80"/>
</ResponseData>
I would recommend using XML that powershell knows how to work with.
[xml]$xml = Get-Content C:\temp\xml.txt
$TypeNode = $xml.SelectSingleNode("//Type[#id='Score']")
$TypeNode.Attributes["value"].Value

XML - Check for existence of a specific node

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

In C# is there anyway to read a property directly (versus having to loop through PropertyNames)

I have the following code:
// Create a DirectorySearcher object.
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.SearchScope = SearchScope.Base;
// Use the FindOne method to find the user object.
SearchResult resEnt = mySearcher.FindOne();
foreach(string propKey in resEnt.Properties.PropertyNames)
{
foreach (var property in resEnt.Properties[propKey])
{
Console.WriteLine("{0}:{1}", propKey, property.ToString());
}
}
This works fine but I only need to get a single property called "mail". Is there anyway i can just read a single property without having to loop. I am looking for something like this:
var emailAddress = resEnt.Properties["mail"];
You probably want:
string emailAddress = (string)resEnt.Properties["mail"][0];
Note that you might want to do some checking here to make sure there is a valid "mail" property:
var mailProps = resEnt.Properties["mail"];
string emailAddress = mailProps.Count > 0 ? (string)mailProps[0] : string.Empty;
Updated to work
The example you have will return a single Property. The problem is that the return is a Collection. I recommend doing
// Or String if you know it's always a string
var mailCollection = resEnt.Properties["mail"].Cast<Object>();
var emailAddress = mailCollection.FirstOrDefault();
If you use FirstOrDefault it will return the first value or null. If you use Single or [0] you will have to perform a validation step before hand or catch the Exception that is thrown when no results are returned.
Use the cast operation in conjuntion with LINQ to get the first object.
var email = resEnt.Properties["mail"].Cast<object>.FirstOrDefault();

Why this linq query returns only first attribute?

I have a complicated xml file and in different levels the following part may exist:
<ChrNote>
<note>The appropriate character is:</note>
</ChrNote>
<ChrDef>
<extension char="A">initial</extension>
<extension char="D">subsequent</extension>
<extension char="G">subsequent delayed</extension>
<extension char="S">sequel </extension>
</ChrDef>
...
and here is the code snippet that I use to build a text file:
var lv1s = from lv1 in XMLDoc.Descendants("chapter").DescendantsAndSelf()
select new
{
SChar = (string)lv1.Element("ChrNote") ?? "",
SCharDef = (string)lv1.Element("ChrDef") ?? "",
//Returns only first attribute ..?
CharLetter = (lv1.Element("ChrDef") == null ? "" :
(string)(lv1.Element("ChrDef")
.Element("extension")
.Attribute("char")) ?? "")
};
The problem is that the above query returns only the first attribute ("A") from element "extension". I have no experience in linq and any help will be appreciated.
(I know that your question says "returns only the first attribute", but there is only one attribute there -- so I'm answering your implied question instead.)
It's only returning the first extension element because you're using Element("extension"). If you used Elements("extension") instead, you'd get the others.
See the documentation:
http://msdn.microsoft.com/en-us/library/system.xml.linq.xcontainer.element.aspx
http://msdn.microsoft.com/en-us/library/bb348975.aspx
Is this what you are looking for?
var result = from x in XMLDoc.Descendants("chapter")
let Definitions = x.XPathSelectElements("ChrDef/extension")
select new
{
Note = x.XPathSelectElement("ChrNote/note") == null ? "" : x.XPathSelectElement("ChrNote/note").Value,
Definitions = Definitions.Select(y=> new { Extension = y.Value, Char = y.Attribute("char").Value })
};
Console.WriteLine ("{0}", result.First().Note);
foreach (var definition in result.First().Definitions)
{
Console.WriteLine ("{0}, {1}", definition.Extension, definition.Char);
}
This will give you the following output:
The appropriate character is:
initial, A
subsequent, D
subsequent delayed, G
sequel , S

Return Guid From XLinq attribute value, or Guid.Empty if XElement/XAttribute not found

I am trying to use Linq to get a Guid from an XAttribute value ...
XDocument __xld = XDocument.Parse(
"<Form sGuid='f6b34eeb-935f-4832-9ddc-029fdcf2240e'
sCurrentName='MyForm' />");
string sFormName = "MyForm";
Guid guidForm = new Guid(
__xld.Descendants("Form")
.FirstOrDefault(xle => xle.Attribute("sCurrentName").Value == sFormName)
.Attribute("sGuid").Value
);
the thing is, i would like to return Guid.Empty if the XAttribute is missing, or if the XElement is not found, (or something goes wrong!) ...
Can i one-liner this concept, or do i need to execute the query first to see if an XElement has been found with a matching sCurrentName and return Guid.Empty if the query returns nothing ...
UPDATE
Thanks to Miroprocessor, i have ended up with the following ...
Guid guidForm = new Guid(
(from xle in __xld.Descendants("Form")
where xle.Attribute("sCurrentName") != null && xle.Attribute("sCurrentName").Value == sFormName
select xle.Attribute("sGuid").Value).DefaultIfEmpty(Guid.Empty.ToString()).FirstOrDefault()
);
BUT(!) i think the Guid.Empty.ToString() could be avoided if i could create the Guid inside the query (if that is possible).
try
var guidForm =(from xle in __xld.Descendants("Form")
where xle.Attribute("sCurrentName").Value == sFormName
select new {Value = xle.Attribute("sGuid").Value==null?Guid.Empty:new Guid(xle.Attribute("sGuid").Value)}).Single();
so to access the result you will write guidForm.Value
or try that
Guid guidForm =new Guid(from xle in __xld.Descendants("Form")
where xle.Attribute("sCurrentName").Value == sFormName
select xle.Attribute("sGuid").Value==null?Guid.Empty:xle.Attribute("sGuid").Value).Single());
but I am not sure that will work correctly

Categories