DataSet.WriteXml() - how to "drop" some fields - asp.net

I've 2 questions:
First, I've a dataset with 5 tables. I've made the relationships of tables and generating an XML from this dataset like this:
StreamWriter xmlDoc = new StreamWriter("myxml.xml", false);
ds.WriteXml(xmlDoc);
xmlDoc.Close();
Some of the fields in each table in dataset are primary keys and I dont want to show them in XML. If I exclude them from tables, I cant make relationships. Can anybody give me some idea that how to write dataset to XML "droping" the key fields (columns in datatables)? For example, here is the XML generating at the moment:
<?xml version="1.0"?>
<o>
<sp spname="SP1" spid="8">
<event spid="8" eventname="Event1" eventId="482">
<bm bmname="BM1" bmid="2" bmeid="826" eventid="482">
<att bmeid="826" val="3.00" attname="Att1" atttype="Type1" attid="23172"/>
<att bmeid="826" val="3.50" attname="Att2" bettype="Type1" attid="23173"/>
</bm>
</event>
</sp>
</o>
but I want this XML to be generated like this (all id attributes should be "dropped" as all ids are for relationships and should not be added in XML):
<?xml version="1.0"?>
<o>
<sp spname="SP1">
<event eventname="Event1">
<bm bmname="BM1" bmid="2">
<att val="3.00" attname="Att1" atttype="Type1" />
<att val="3.50" attname="Att2" bettype="Type1" />
</bm>
</event>
</sp>
</o>
And now 2nd question:
I've given name to my dataset as "o" so its generating the xml as you can see above. I want to add some attributes to <o> node like current datetime. I mean I want <o> node to be genrated as <o generatedDate="09/13/2011" generatedTime="03:45 PM">. How can I achieve it?
Thanks,

You can hide the column to exclude it from the XML-File:
dataSet.Tables["TableName"].Columns["ColumnName"].ColumnMapping = MappingType.Hidden;

One option is to use LINQ to XML to alter the document. Another option would be to pass the XML into an XSLT transformer, which can parse the XML and output the desired results.
Transforming a DataSet using XSLT:
DataTable table = new DataTable();
System.IO.StringWriter writer = new System.IO.StringWriter();
//notice that we're ignoring the schema so we get clean XML back
//you can change the write mode as needed to get your result
table.WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
string dataTableXml = writer.ToString();
As for displaying it in a readable format, I would suggest passing the XML into an XSL transformer, which you can then use to parse the XML and manipulate the output as needed.
Applying an XSLT Transform to a DataSet
http://msdn.microsoft.com/en-us/library/8fd7xytc%28v=vs.71%29.aspx#Y289
Here's a simple example I created to explain how you would use the XSL transformer. I haven't tested it, but it should be pretty close:
DataSet ds = new DataSet();
StringBuilder sbXslOutput = new StringBuilder();
using (XmlWriter xslWriter = XmlWriter.Create(sbXslOutput))
{
XslCompiledTransform transformer = new XslCompiledTransform();
transformer.Load("transformer.xsl");
XsltArgumentList args = new XsltArgumentList();
transformer.Transform(new XmlDataDocument(ds), args, xslWriter);
}
string dataSetHtml = sbXslOutput.ToString();
Formatting XML as HTML using XSLT
Here's an example of using XSLT to transform XML into an HTML table. It should be fairly easy to adopt so you can use it with your serialized DataSet.
Let's say this is your DataSet, serialized to XML:
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>99999-2302</MatterNumber>
<ClientName>Test Matters</ClientName>
<MatterName>DP Test Matter</MatterName>
<ClientCode>99999</ClientCode>
<OfficeCode/>
<OfficeName/>
<Billable>true</Billable>
<ReferenceId/>
<LastUsed>2011-08-23T23:40:24.13+01:00</LastUsed>
</RecentMatter>
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>999991.0002</MatterNumber>
<ClientName>Lathe 1</ClientName>
<MatterName>LW Test 2</MatterName>
<ClientCode/>
<OfficeCode/>
<OfficeName/>
<Billable>true</Billable>
<ReferenceId/>
<LastUsed>2011-07-12T16:57:27.173+01:00</LastUsed>
</RecentMatter>
<RecentMatter>
<UserLogin>PSLTP6\RJK</UserLogin>
<MatterNumber>999991-0001</MatterNumber>
<ClientName>Lathe 1</ClientName>
<MatterName>LW Test 1</MatterName>
<ClientCode/>
<OfficeCode/>
<OfficeName/>
<Billable>false</Billable>
<ReferenceId/>
<LastUsed>2011-07-12T01:59:06.887+01:00</LastUsed>
</RecentMatter>
</NewDataSet>
Here's an XSLT script that transforms the DataSet to HTML:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<table border="1">
<tr>
<th>User Login</th>
<th>Matter Number</th>
...
</tr>
<xsl:for-each select="NewDataSet/RecentMatter">
<tr>
<td>
<xsl:value-of select="UserLogin"/>
</td>
<td>
<xsl:value-of select="MatterNumber"/>
</td>
...
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>

I would suggest after generating the XML, use LINQ to XML to filter out / add attribute to desired nodes. This filtering and adding attribute is a separate step then generating the XML from data set and should be handled after the XML is generated from data set as that would lead to better design.

Related

How do I retrieve embedded XML values using MSXML in AutoItV3?

I am trying to use AutoItV3 to automate the insertion of some Entities into a piece of software.
It will be far easier if my automation can read information from an xml file and use this to generate my entities, as I can then parse in different xml files for different tests.
I am using a popular extension MSXML to try and do this. This can be found here:
https://www.autoitscript.com/forum/applications/core/interface/file/attachment.php?id=44418
My XML is a relatively simply structure where I will have various fields under each 'Entity' within all of my 'Entities'
<?xml version="1.0" encoding="UTF-8"?>
<entities>
<entity>
<name>
Mation Jr, Mr Auto
</name>
<legalname>
Mr Auto Mation Jr
</legalname>
</entity>
<entity>
<name>
Mation Sr, Mr Auto
</name>
<legalname>
Mr Auto Mation Sr
</legalname>
</entity>
</entities>
In my script header I am importing the MSXML au3 file and setting the XML path
#include <_MSXML.au3>
; Set the XML file
$xmlpath = #ScriptDir & "\Entity.xml"
My Question is, how can I iterate through the attributes of each Entity within all Entities?
This is what I have so far, but i am not understanding how I would retrieve values from an individual entity listed under the Entities node:
; Fetch All Entities from XAML
$ENTITIES = _MSXML_SelectNodes($oXml, "entities/entity")
If ($ENTITIES[0] > 0) Then
; This part works and will iterate for x amount of entities provided
; Fetch Entity as pos $i
For $i = 1 To $ENTITIES[0] Step 1
; How can I iterate through attributes from ENTITIES[$i] ??
Next
Else
MsgBox(4096, 'Error', 'No entity was provided')
EndIf
I understand my question is quite broad but I think there should be enough information to start with
This issue with that UDF is that it seems to want to return strings for everything instead of xml objects which are more useful. I would avoid it and instead just just use the com object yourself with $oXml = ObjCreate("Msxml2.DOMDocument") and then have a look at the documentation here.
But anyways, I think this code will get you what you want:
; Set the XML file
$xmlpath = #ScriptDir & "\Entity.xml"
$oXml = ObjCreate("Msxml2.DOMDocument")
$oXml.load($xmlpath)
; Fetch All Entities from XAML
$objNodeList = $oXml.selectNodes("entities/entity")
For $node in $objNodeList
ConsoleWrite($node.nodename & #CRLF)
$objChildNodeList = $node.selectNodes("*")
For $ChildNode in $objChildNodeList
ConsoleWrite(#TAB & $ChildNode.nodename & ' = ' & $ChildNode.text & #CRLF)
Next
Next
Notice how there is really no need to use a UDF and you can just use the com object's built in methods. In my opinion, this is simpler than using the UDF.
Another thing in general that is worth mentioning is that if you ever have trouble figuring out how to do anything in autoit you can try searching for how to do that same thing in vba or vbs since the languages are pretty similar and autoit can use all the com objects that are used in vba/vbs. When vba/vbs does something like this Set oXml = CreateObject("Msxml2.DOMDocument") just do this in autoit: $oXml = ObjCreate("Msxml2.DOMDocument").

XmlReader how to read or skip a specific child that does not always exist

I have a big XML file that I must read with XmlReader because it can not be loaded into memory. This XML is formatted in this way (is a reduced version):
<?xml version="1.0" encoding="windows-1252"?>
<Products>
<Product>
<Code>A14</Code>
<Name>Name1</Name>
<Manufacturer>
<Name>ManufacturerName</Name>
</Manufacturer>
<ProdCategories>
<ProdCategory>
<Code>015</Code>
<Name>ProdCategoryName</Name>
</ProdCategory>
</ProdCategories>
<Barcodes> <!-- note this line -->
</Barcodes>
</Product>
<Product>
<Code>A15</Code>
<Name>Name2</Name>
<Manufacturer>
<Name>ManufacturerName</Name>
</Manufacturer>
<ProdCategories>
<ProdCategory>
<Code>016</Code>
<Name>ProdCategoryName</Name>
</ProdCategory>
</ProdCategories>
<Barcodes>
<Barcode>
<Code>1234567890</Code> <!-- note this line -->
</Brcode>
</Barcodes>
</Product>
Note the <Barcode> <Code> elements: in the first <product> is missing.
This is the code that I use for read it and for put these data in a database:
XmlReader reader = XmlReader.Create("Products.xml");
reader.MoveToContent();
do
{
reader.ReadToFollowing("Code");
code = reader.ReadElementContentAsString();
reader.ReadToFollowing("Name");
Name = reader.ReadElementContentAsString();
reader.ReadToFollowing("Name");
ManufacturerName = reader.ReadElementContentAsString();
reader.ReadToFollowing("Code");
ProdCategoryCode = reader.ReadElementContentAsString();
reader.ReadToFollowing("Code");
BarcodeCode = reader.ReadElementContentAsString();
//Here I use "code", "Name", "ManufacturerName" variables to insert into a database
} while (reader.Read());
reader.Close();
All XML tags are present in all products except the <Barcodes> childs (<Barcode><Code>) that is present only on some product, then I cannot jump at next "code" with last ReadToFollowing because if not present I capture the first <product><code>.
I cant control XML output and cant modify it (is third-party).
There's a way to "ReadToFollowing('<Barcodes><Barcode><Code>')" so that I can specific what should seek and if there is not found I can jump it?
Thank you for your help, excuse my bad english.
I would suggest to pull each Product element into a tree model, using either https://msdn.microsoft.com/en-us/library/system.xml.linq.xnode.readfrom(v=vs.110).aspx or https://msdn.microsoft.com/en-us/library/system.xml.xmldocument.readnode(v=vs.110).aspx, then you can use LINQ to XML query methods or XPath to read out the data of each Product in a safe way while maintaining a low memory footprint.

Using XQUERY to retrieve attributes value

Is it possible to use XQUERY to retrieve the attributes filename from the following XML? I am trying to use /preFileDoc/inpXML/#filename but it doesn't work...
<?xml version="1.0"?>
<preFileDoc xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<senderId>ABC</senderId>
<receiverId>XYZ</receiverId>
<tranxCode>A001</tranxCode>
<inpXML version="1.0" encoding="UTF-8">
<soap-env:Envelope>
<soap-env:Header msgcode="SPPCONVAKT" orig-system="002FTB" refid="65355ff50a172064484bf9da64c1e245" timestamp="2009-02-11 21:00:10.741" filename="SPPCONVAKT20090128001.dat"/>
<soap-env:Body>
text1
text2
</soap-env:Body>
</soap-env:Envelope>
</inpXML>
</preFileDoc>
ps: Sometimes the filename attributes is sent as fileName in the incoming XML..thinking to retrieve value from attributes #filename OR #fileName.. can it achieve in single XQUERY? Thanks for advice...
I think your XPath is incomplete. The last child-step / in /preFileDoc/inpXML/#filename only matches attributes of the inpXML element, not its descendants.
One way to solve the problem would be the //-step:
/preFileDoc/inpXML//#filename
Note that this would find all attributes named filename in the soapenv:Body, too.
A more robust way would thus be to declare the soapenv prefix in the XQuery:
declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";
return /preFileDoc/inpXML//soap-env:Header/#filename
Finally, the different capitalizations of filename can be worked around by specifying both:
declare namespace soap-env="http://schemas.xmlsoap.org/soap/envelope/";
return /preFileDoc/inpXML//soap-env:Header/(#filename | #fileName)
You can take the union of multiple attributes. It will be unlikely that this attribute will appear multiple times with different casing, so that should always return a single node:
//soap-env:Header/#filename | //soap-env:Header/#fileName
Optionally, you could wrap it in parentheses, and add [1] behind it, to always take the first result.
(//soap-env:Header/#filename | //soap-env:Header/#fileName)[1]
If you replace the union with a comma, which creates a sequence instead of a document order node set, you can add a default as well at the end. Maybe not very usefull here, but perhaps in other situations:
(//soap-env:Header/#filename , //soap-env:Header/#fileName, "default.dat")[1]
HTH!
You need to respect and take into account the SOAP XML namespace!
Since I don't know what you're using, I cannot tell you how to do this - but there's the xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" on the root node, and your #filename attribute is on the <soap-env:Header .... /> node - so you need to include the XML namespace in your XQuery.
In .NET / C#, you could do it like this (using the "older" XmlDocument style which supports XPath directly):
// define test XML
string xmlContent =
#"<?xml version='1.0'?>
<preFileDoc xmlns:soap-env='http://schemas.xmlsoap.org/soap/envelope/'>
<senderId>ABC</senderId>
<receiverId>XYZ</receiverId>
<tranxCode>A001</tranxCode>
<inpXML version='1.0' encoding='UTF-8'>
<soap-env:Envelope>
<soap-env:Header msgcode='SPPCONVAKT' orig-system='002FTB' refid='65355ff50a172064484bf9da64c1e245' timestamp='2009-02-11 21:00:10.741' filename='SPPCONVAKT20090128001.dat'/>
<soap-env:Body>
text1
text2
</soap-env:Body>
</soap-env:Envelope>
</inpXML>
</preFileDoc>";
// create XmlDocument and load test data
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
// define XML namespace manager and add the SOAP namespace to it
XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// use XPath and the XML namespaces to grab the <Header> node
// the first two nodes <preFileDoc> and <inpXML> are not inside any explicit
// XML namespace
// but the next two (<Envelope> and <Header>) are in the "soap" XML namespace
XmlNode header = doc.SelectSingleNode("/preFileDoc/inpXML/soap:Envelope/soap:Header", mgr);
// read the "filename" attribute from the header node
if(header != null && header.Attributes["filename"] != null)
{
string fileName = header.Attributes["filename"].Value;
}

Create csv with asp.net and open in excel

I'm using Asp.net to create a csv file that the user can open directly in excel. I want to make it possible to show the download popup and for the user to choose "Open with Excel" and that will open the file in excel.
The code to create the csv:
Response.Clear();
Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.csv", "test"));
Response.ContentType = "text/csv";
Response.Write("Year, Make, Model, Length\n1997, Ford, E350, 2.34\n2000, Mercury, Cougar, 2.38");
Response.End();
Excel needs to understand that "Year", "Make", etc should all be different columns. That doesn't seem to work with the csv I'm creating, everything is just in the same column. It shows up like this:
http://oi54.tinypic.com/2evyb0k.jpg
The only way to get it working with excel is to use the "Text Import Wizard". Then everything shows up in different columns like it should.
As I said what I need is to create a spreadsheet (it doesn't need to be csv), it should just be easy for the user to open in excel or whatever they are using to start working with it. How would you solve this? Thanks!
Not sure why it doesn't work like that - it does for me, but at a minimum try quoting the data:
Response.Write("\"Year\", \"Make\", \"Model\", \"Length\"\n\"1997\", \"Ford\", \"E350\", \"2.34\"\n\"2000\", \"Mercury\", \"Cougar\", \"2.38\"");
Excel can also import XML, google "<?mso-application progid="Excel.Sheet"?>" for the format. Or just save an excel spreadsheet as XML to see an example. Excel XML files can be named ".xls" and it will be able to open them directly.
Finally, you can use NPOI in .NET to manipulate excel spreadsheets in the native format: http://npoi.codeplex.com/
For XML you can also use a wrapper class I wrote that encapsulates this in a simple C# object model: http://snipt.org/lLok/
Here's some pseudocode for using the class:
XlsWorkbook book = new XlsWorkbook();
XlsWorksheet ws = new XlsWorksheet();
ws.Name = "Sheet Name";
XlsRow row;
XlsCell cell;
foreach (datarow in datasource) {
row = new XlsRow();
foreach (datavalue in datarow) {
cell = new XlsCell(datavalue.ToString());
cell.DataType = datavalue is numeric ? DataTypes.Numeric | DataTypes.String;
row.Cells.Add(cell);
}
ws.Rows.Add(row);
}
wkb.Worksheets.Add(ws);
Response.Write(wkb.XmlOutput());
Or just create your own method, here are the basics.
Create the header this way:
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("<?xml version=\"1.0\"?>\n");
sb.Append("<?mso-application progid=\"Excel.Sheet\"?>\n");
sb.Append(
"<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" ");
sb.Append("xmlns:o=\"urn:schemas-microsoft-com:office:office\" ");
sb.Append("xmlns:x=\"urn:schemas-microsoft-com:office:excel\" ");
sb.Append("xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" ");
sb.Append("xmlns:html=\"http://www.w3.org/TR/REC-html40\">\n");
sb.Append(
"<DocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">");
sb.Append("</DocumentProperties>");
sb.Append(
"<ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">\n");
sb.Append("<ProtectStructure>False</ProtectStructure>\n");
sb.Append("<ProtectWindows>False</ProtectWindows>\n");
sb.Append("</ExcelWorkbook>\n");
From there, add to your output string by creating nodes using this basic structure:
<Worksheet ss:Name="SheetName">
<Table>
<Row>
<Cell>
<Data ss:Type="DataType">Cell A1 Contents Go Here</Data>
</Cell>
</Row>
</Table>
</Worksheet>
Within a <Row>, each <Cell> element creates a new column. Add a new <Row> for each spreadsheet row. The DataType for <Data> can be "String" or "Number".

how can I read an xml attribute using readXML? how does dataset.readxml translate into tables?

I just want to know how does the table resulting from readXML look like, say if the xml file looks like this:
<item attr="some attribute">
<descirption>anything</description>
</item>
I can reference tables directly by the Tables collection like this:
ds.ReadXml(xml);
... ds.Tables[i]
then I can access rows and columns using the rows collection:
ds.Tables[3].Rows[i].ItemArray(j);
but how can I access an "attribute" of any xml node?
It's in the Row. I fixed your xml a bit :)
var xml = "<item attr=\"some attribute\"><description>anything</description></item>";
var ds = new DataSet();
ds.ReadXml( new StringReader( xml ), XmlReadMode.Auto );
var ia = ds.Tables[0].Rows[0].ItemArray;
var att = ia[1]; // att == "some attribute"
If you don't have a schema, you might have to check the column to determine what it is.
Per comment: You will see I am letting it infer the schema (XmlReadMode.Auto). It takes elements under the root node as Rows then adds the attributes in order and then the value in the element. So for example the following XML ...
var xml = "<items>
<item attr1='attr1' attr2='attr2'>
<description>desc1</description>
</item>
<item attr1='attr3' attr2='attr4'>
<description>desc2</description></item>
</items>";
I will get two rows (one for each item) with Columns for attr1, attr2 and description. You can change the way it interprets the XML using a schema.

Resources