Removing header in xml file required? - c#

I have the xml file below and I want to retrieve the sym attribute from the Instrmt element using LINQ to XML. In order to do that, do I have to remove the first line: FIXML element?
If I do that then the code below works:
IEnumerable<string> symbols = from c in xml.Descendants("Instrmt")
select (string)c.Attribute("Sym");
Here is the xml.
<FIXML r="20030618" s="20040109" v="4.4" xr="FIA" xv="1" xmlns="http://www.fixprotocol.org/FIXML-4-4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.fixprotocol.org/FIXML-4-4 https://optionsclearing.com/components/docs/membership/dds_ref/fia_1_1/fixml-main-4-4-FIA-1-1.xsd">
<Batch>
<SecList ListTyp="109" ListID="20175" BizDt="2017-12-07">
<SecL Ccy="USD">
<Instrmt Desc="iShares S&P 100 ETF" SecTyp="OPT" SubTyp="ETO" Sym="OEF" Mult="100.0">
<AID AltID="00013" AltIDSrc="RBHP"/>
</Instrmt>
<InstrmtExt>
<Attrb Typ="101" Val="1.0000"/>
<Attrb Typ="108" Val="1.0000"/>
</InstrmtExt>
<Undly Desc="iShares S&P 100 ETF" Px="117.110000" Ccy="USD" Sym="OEF" ID="464287101" Src="1"/>
<Stip Typ="RBHMIN" Val="2.500"/>
<Stip Typ="CPMMIN" Val="3.750"/>
</SecL>
</SecList>
</Batch>
</FIXML>

Ok, your problem is not about the header, it's about the namespace (defined by the xmlns attribute). As your xml define one, all your xml children elements will have it as their default. Try the following code:
var xml = XDocument.Load(pathToFile);
XNamespace ns = xml.Root.GetDefaultNamespace();
IEnumerable<string> symbols = from c in xml.Descendants(ns + "Instrmt")
select (string)c.Attribute("Sym");

Related

I am having an xml inside xml and want to test a condition is met inside the inner xml. C# solution required

<?xml version="1.0"?>
<TextType IsKey="false" Name="XMLReport"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Providers
xmlns="Reporting"/>
<Sales
xmlns="Reporting"/>
<Value
xmlns="Reporting">
<?xml version="1.0" encoding="utf-8"?>
<TestReport>
<StudyUid>
<![CDATA[123]]>
</StudyUid>
<Modality>
<![CDATA[XYZ]]>
</Modality>
<StudyDate format="DICOM">123456</StudyDate>
<StudyTime format="DICOM">6789</StudyTime>
<AccessionNumber>
<![CDATA[123]]>
</AccessionNumber>
<StudyDescription>
<![CDATA[abc def]]>
</StudyDescription>
<OperatorName format="xyz">
<![CDATA[abc]]>
</OperatorName>
<PhysicianReadingStudy format="xyz">
<![CDATA[^^^^]]>
</PhysicianReadingStudy>
<InstitutionName>
<![CDATA[xyz]]>
</InstitutionName>
<HospitalName>
<![CDATA[Hospital Name]]>
</HospitalName>
<ReportSet>
<MyReport ID="1">
<ReportStatus>
<![CDATA[Done]]>
</ReportStatus>
</MyReport>
<MyReport ID="2">
<ReportStatus>
<![CDATA[Done]]>
</ReportStatus>
</MyReport>
<MyReport ID="3">
<ReportStatus>
<![CDATA[Initial]]>
</ReportStatus>
</MyReport>
</ReportSet>
<ReportImageSet />
<FetusSet />
</TestReport>
</Value>
<WhoSetMe xmlns="Reporting">NotSpecified
</WhoSetMe>
</TextType>
I want to parse the xml above in C# and check whether "ReportStatus" is "Done" for all the ReportStatus under MyReport/ReportSet. One more twist here is the xml contains one more xml starts at "Value" tag as in above example.It may contatin many ReportStatus tag under ReportSet tag. Can someone please help me?
// Can you try this? I tried to do it with LINQ to XML.
// I assume you have multiple <TestReport /> elements in <Value /> tag
// and var xelement is your xml variable
// First we get all TestReport elemnts
IEnumerable<XElement> allReports =
from el in xelement.Elements("TextType/Value/TestReport")
select el;
// From allReports we get all MyReport elemnts
IEnumerable<XElement> allMyReports =
from el in allReports.Elements("ReportSet/MyReport")
select el;
// From allReports we also get all MyReport elemnts with element ReportStatus value equals "Done"
IEnumerable<XElement> allDoneMyReports =
from el in allMyReports
where (string)el.Element("ReportStatus") == "Done"
select el;
// Now we compare allMyReport with allDoneMyReports
if (allMyReports.Count() == allDoneMyReports.Count())
{
//DO Somehing
}
Your XML document is invalid. You need to fix it before trying to parse it. The issue is that a document can only have one top-level element; you have 2 <TextType> and <Providers>.
Most of your elements are the namespace Reporting. You need to use it when referencing the element.
XNamespace ns = "Reporting";
var value = doc.Element("Value" + ns);
Update
Just use the namespace for each element
XNamespace ns = "Reporting";
var value = xelement.Elements("Value" + ns);
Another Update
The XML document is considered invalid because it has multiple XML declarations; there is no way to disable this. I suggest you pre-process the document to remove the extra declarations. Here's an example (https://dotnetfiddle.net/UnuAF6)
var xml = "<?xml version='1.0'?><a> <?xml version='1.0'?><b id='b' /></a>";
var doc = XDocument.Parse(xml.Replace(" <?xml version='1.0'?", " "));
var bs = doc.Descendants("b");
Console.WriteLine("{0} 'b' elements", bs.Count());

System.InvalidOperationException when modifiying value in an xml file in C#

So i... Have this snippet of code what writes to an existing xml file... the code to me is VERY simple...
XElement element;
XDocument xdoc = XDocument.Load(FileLoc);
element = xdoc.Elements(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).Single();
element.Value = Gold.Text;
Good Right? good! but why does it give out that error which means that it can't find the thing? it's a very valid thing....
here is the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Save xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/DumaLegend">
<saveInfo>
<energyPieces>0</energyPieces>
<fullEnergyCells>4</fullEnergyCells>
<fullHearts>4</fullHearts>
<globalSwitches xmlns:d3p1="a">
<d3p1:switchList xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</globalSwitches>
<gold>0</gold>
<hasBigFireball>false</hasBigFireball>
<hasCombo>false</hasCombo>
<hasCrossbow>false</hasCrossbow>
<hasDash>false</hasDash>
<hasDashUpgrade>false</hasDashUpgrade>
<hasDoubleJump>false</hasDoubleJump>
<hasFireball>false</hasFireball>
<hasHookshot>false</hasHookshot>
<hasInvisPot>false</hasInvisPot>
<hasSecondCombo>false</hasSecondCombo>
<hasShieldUpgrade>false</hasShieldUpgrade>
<hasSmallFireball>false</hasSmallFireball>
<heartPieces>0</heartPieces>
<heroPosOnMap>0</heroPosOnMap>
<heroTokens>0</heroTokens>
<itemSlot1 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<itemSlot2 xmlns:d3p1="http://schemas.datacontract.org/2004/07/DumaLegend.Objects.Consumables" i:nil="true" />
<lives>3</lives>
<worldsUnlocked>0</worldsUnlocked>
<worldsUnlockedOnMap>0</worldsUnlockedOnMap>
</saveInfo>
<saveSlot>0</saveSlot>
</Save>
Use xdoc.Descendants(XName.Get("gold", "http://schemas.datacontract.org/2004/07/DumaLegend")).
From the docs for Elements
Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching XName are included in the collection.
There is only one child elements of your document, and that is the Save element.
What you are looking for is at the path Save/saveInfo/gold. So you can either use Elements like this:
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Elements(ns + "Save")
.Elements(ns + "saveInfo")
.Elements(ns + "gold")
.Single();
Or you can use Descendants, which will search all child elements recursively.
XNamespace ns = "http://schemas.datacontract.org/2004/07/DumaLegend";
var gold = doc.Descendants(ns + "gold").Single();

how to add xmlns attribute to an existing xml document

I have existing xml document.
e.g
<Test>
<A />
</Test>
I load this xml into XDocument. I need to add attribute xmlns to this document and save it with this attribute.
var xml = new XDocument.Load("c:\\filePath.xml");
When I'm trying this:
xml.Root.SetAttributeValue("xmlns", "http://namespaceuri");
I'm getting exception:
System.Xml.XmlException: The prefix '' cannot be redefined from 'http://namespaceuri' to within the same start element tag.
thanks
You need to set the names to be in the namespace as well:
XNamespace ns = "http://namespaceuri";
foreach (var element in xml.Descendants().ToList())
{
element.Name = ns + element.Name.LocalName;
}
xml.Root.SetAttributeValue("xmlns", ns.ToString());
Basically you're trying to move all the elements to that namespace and make it the default namespace for the root element down. You can't change the default namespace while leaving the element itself in a different-but-unqualified namespace.
Using the code above with your sample XML (fixed to close A) ends up with:
<Test xmlns="http://namespaceuri">
<A />
</Test>
Note that this code will change the namespace of all elements. If you want to be more selective, you should add a Where call after the xml.Descendants() call, e.g.
foreach (var element in xml.Descendants()
.Where(x => x.Name.Namespace == XNamespace.None)
.ToList())

Can't select nodes in XML document

My XML document is the Corpus.xml file in the TEI XML Brown Corpus
I want to select every s node in the document, so I can iterate over them and extract data.
The problem is that no matter which method I try, I cannot select the s nodes! I've tried:
xml.Root.Descendants("s")
xml.Root.XPathSelectElements("s")
XPathDocument
But nothing works. I get no errors. The result returned is an empty set!
Your XML documents has namespace declared:
<?xml version="1.0"?>
<teiCorpus xmlns="http://www.tei-c.org/ns/1.0"
xmlns:xi="http://www.w3.org/2001/XInclude">
<!-- content -->
</teiCorpus>
So you should use XNamespace to select elements:
var xdoc = XDocument.Load(path_to_xml);
XNamespace ns = "http://www.tei-c.org/ns/1.0";
var elements = xdoc.Descendants(ns + "s");
Take a look on Working with XML Namespaces for further information.

Reading XML with XDocument problem

I have a following XML (part of a .rdl report):
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition">
<DataSources>
<DataSource Name="TMSSharepointDataSource">
<DataSourceReference>TMSSharepointDataSource</DataSourceReference>
<rd:DataSourceID>f06ffa33-238f-4d83-adfe-1eaa8df96e90</rd:DataSourceID>
</DataSource>
</DataSources>
</Report>
I try to parse and read it using following code:
byte[] fileContent = File.ReadAllBytes(#"path");
UTF8Encoding unicode = new UTF8Encoding();
string stringContent = unicode.GetString(fileContent);
XDocument xml = XDocument.Parse(stringContent);
XElement dsNode = xml.Root.Element("DataSources");
I can't figure out why is dsNode always null?
It's a namespace issue... you need to specify the namespace for the DataSources element. Fortunately, LINQ to XML makes this really easy:
XNamespace ns = "http://schemas.microsoft.com/sqlserver/" +
"reporting/2008/01/reportdefinition";
XElement dsNode = xml.Root.Element(ns + "DataSources");
Note the xmlns="http://..." part of the root element, which means that element and all elements below it which don't have an explicit namespace inherit that namespace.
You are probably missing a namespace reference. Your DataSources will inherit the namespace of the Report node and you will need both the namespace and element local name to generate an XName.
Alternatively you can do the following and skip the namespace check:
XElement dsNode =
xml
.Root
.DescendantNodes()
.Where(e => e.Name.LocalName.Equals("DataSources"))
.First();
This will return the first node where the local name is DataSources. In your example this will be the DataSources element.
Also, your loading of the document is very clumsy. I would suggest the following instead:
XDocument xml = XDocument.Load(File.OpenRead(#"path"));

Categories