Can anyone point me in the rigth direction for a solution, to get data from a xml file. I normaly use XmlDocument in VB applications, and that have worked flawless all times.
Now Windows Store Apps not really supporting xmlDocument anymore, som working when DOM is imported but XmlNode is not availble. However maybe its just me but XDocument seems to me to be very very difficult and not very logic for looking up data in a simple XML file.
before i used some like this:
xmlDevices = xmlDoc.GetElementsByTagName("Device")
For Each xmlDevice As xmlElement In xmlDevices
Dim strDeviceName As String = xmlDevice.Item("DeviceName").InnerText
xmlModbuses = xmlDoc.GetElementsByTagName("Modbus")
For Each xmlModbus As xmlElement In xmlModbuses
Dim strModbusID As String = xmlModbus.Attributes("id").InnerText
Next
Next
The XML file i wants to seek data from looks like:
<?xml version="1.0" encoding="utf-8" ?>
<Devices>
<Device id="01">
<DeviceName>VP18</DeviceName>
<Modbusees>
<Modbus id="01">1000</Modbus>
<Modbus id="02">2000</Modbus>
...
</Modbuses>
<Alarms>
<Alarm id="01">
<AlarmText>Test</AlarmText>
<AlarmType>Critical</AlarmType>
</Alarm>
<Alarm id="02">
<AlarmText>Test</AlarmText>
<AlarmType>Critical</AlarmType>
</Alarm>
</Alarms>
<Device id="02">
<DeviceName>VP19</DeviceName>
<Modbusees>
<Modbus id="01">1010</Modbus>
<Modbus id="02">2020</Modbus>
...
</Modbuses>
<Alarms>
<Alarm id="01">
<AlarmText>Test</AlarmText>
<AlarmType>Critical</AlarmType>
</Alarm>
<Alarm id="02">
<AlarmText>Test</AlarmText>
<AlarmType>Critical</AlarmType>
</Alarm>
</Alarms>
</Device>
</Devices>
Best Regards
Thomas Nissen
I got it working with following.
Dim xDoc As XDocument = XDocument.Load(xmlStream.AsStreamForRead())
Dim xmlDevices = xDoc.Root.Elements("Device")
For Each xmlDevice In xmlDevices
If xmlDevice.Attribute("id").Value = RoamingSettings.Containers("Device").Values("DeviceID") Then
Dim xmlAlarms = xmlDevice.Descendants("Alarm")
For Each xmlAlarm In xmlAlarms
If xmlAlarm.Attribute("id").Value = strAlarmID Then
strAlarmDisp = xmlAlarm.Element("AlarmDisp").Value
strAlarmType = xmlAlarm.Element("AlarmType").Value
strAlarmDesc = xmlAlarm.Element("AlarmDesc").Value
strAlarmHelp = xmlAlarm.Element("AlarmHelp").Value
End If
Next
End If
Next
Related
My goal is to fill a LibreOffice calc sheet, and silently send a cell range by email when the user clicks the send-off button (and once more to confirm).
So there is three part to this.
A push button with a request to confirm. (Easy and done.)
Select Cell Range and turn it into rich text format (Haven't yet found)
Send rich text email from within the sheet. (Will tackle the "silent" part later)
I tried copying the range to the clipboard with unoService but it seemed over-complicated and full of errors.
Here's what I have:
''''Send by e-mail enriched text
Sub Main
Dim Doc, Sheet, Range, Rtf, Exec as Object
End Sub
'Confirm it
Sub SendTableApproval
If MsgBox ("Ready to email?", MB_YESNO + MB_DEFBUTTON2) = IDYES Then
CopyTable()
End If
End Sub
'Copy it
Sub CopyTable
Doc = ThisComponent
View = Doc.CurrentController
Frame = View.Frame
Sheet = Doc.Sheets.getByIndex(0)
Range = Sheet.getCellrangeByName("a1:f45")
Exec = createUnoService("com.sun.star.frame.DispatchHelper")
View.Select(Range)
Cells = View.getTransferable()
Exec.executeDispatch(Frame, ".uno:Deselect", "", 0, array())
'SimpleMailTo(Cells)
End Sub
'Mail it
Sub SimpleMailTo(body)
Dim launcher as object
Dim eAddress, eSubject, eBody, aHTMLanchor as string
launcher = CreateUnoService("com.sun.star.system.SystemShellExecute")
eAddress = "tu#domo.eg"
eSubject = "Cotidie agenda futuendane"
eBody = body
aHTMLanchor = "mailto:" & eAddress & "?subject=" & eSubject & "&&body=" & eBody
launcher.execute(aHTMLanchor, "", 0)
End Sub
I still do not know after three days of research over methods, properties, uno.
My question is, simply put, How can I convert a transferable content to HTML/RTF?
Simply copying and pasting into an email produces the result you are asking for. The code on the LibreOffice side should look like this.
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())
It sounds like you already tried this, but something didn't work. Perhaps you could elaborate on what went wrong.
Another approach would be to write the spreadsheet to a temporary HTML or XHTML file. Then parse the temporary file to grab the part needed for the email.
AFAIK there is no such command to turn a cell range into rich text format with UNO. To do it that way, you would need to loop through each text range of each cell, read its formatting properties and then generate the HTML yourself.
EDIT:
Good idea about XTransferable. The following Java code adapted from the DevGuide gets an HTML string and then prints it. I believe this would be a good solution for your needs.
public void displayHTMLFromClipboard()
{
try
{
Object oClipboard = xMCF.createInstanceWithContext(
"com.sun.star.datatransfer.clipboard.SystemClipboard", xContext);
XClipboard xClipboard = (XClipboard)
UnoRuntime.queryInterface(XClipboard.class, oClipboard);
XTransferable xTransferable = xClipboard.getContents();
DataFlavor[] aDflvArr = xTransferable.getTransferDataFlavors();
System.out.println("Available clipboard formats:");
DataFlavor aChosenFlv = null;
for (int i=0;i<aDflvArr.length;i++)
{
System.out.println(
"MimeType: " + aDflvArr[i].MimeType +
" HumanPresentableName: " + aDflvArr[i].HumanPresentableName );
if (aDflvArr[i].MimeType.equals("text/html"))
{
aChosenFlv = aDflvArr[i];
}
}
System.out.println("");
try
{
if (aChosenFlv != null)
{
System.out.println("HTML text on the clipboard...");
Object aData = xTransferable.getTransferData(aChosenFlv);
String s = new String((byte[])aData, Charset.forName("ISO-8859-1"));
System.out.println(s);
}
}
catch (UnsupportedFlavorException exc)
{
exc.printStackTrace();
}
}
catch(com.sun.star.uno.Exception exc)
{
exc.printStackTrace();
}
}
If you plan to use Basic, it might be a good idea to do some research into the proper way to convert bytes. The code I have below seems to work but is probably unreliable and unsafe, and will not work for many languages. A few of my initial attempts crashed before this finally worked.
Sub DisplayClipboardData
oClipboard = createUnoService("com.sun.star.datatransfer.clipboard.SystemClipboard")
xTransferable = oClipboard.getContents()
aDflvArr = xTransferable.getTransferDataFlavors()
For i = LBound(aDflvArr) To UBound(aDflvArr)
If aDflvArr(i).MimeType = "text/html" Then
Dim aData() As Byte
aData = xTransferable.getTransferData(aDflvArr(i))
Dim s As String
For j = LBound(aData) to UBound(aData)
s = s & Chr(aData(j)) 'XXX: Probably a bad way to do this!
Next j
Print(s)
End If
Next
End Sub
One more suggestion: Python might be a better language choice here. In many ways, using Python with LibreOffice is easier than Java. And unlike Basic, Python is powerful enough to comfortably handle byte strings.
I am modifying a HTML file using the HTML Agility Pack.
Here is an example on a HTML file containing tables:
Dim document As New HtmlDocument
Dim tables As Array
document.Load(path_html)
Dim div1 As HtmlNode = HtmlNode.CreateNode("<div></div>")
Dim div2 As HtmlNode = HtmlNode.CreateNode("<div></div>")
tables = document.DocumentNode.Descendants("table").ToArray()
For Each tr As HtmlNode In tables.Descendants("tr").ToArray
tr.AppendChild(div1)
tr.AppendChild(div2)
Next
document.save(path_html)
And here is the result in the HTML file:
<div></div><div></div>
What I would like is:
<div></div>
<div></div>
I think this should be implemented by default as it makes my HTML file unclear.
I saw this question (which is my exact issue) here but the answer is not working for me (maybe because of VB.NET and the answer is C#).
Can anyone help?
Haven't written any vb.net in a long time, so first tried this in C#:
var document = new HtmlDocument();
var div = HtmlNode.CreateNode("<div></div>");
var newline = HtmlNode.CreateNode("\r\n");
div.AppendChild(newline);
for (int i = 0; i < 2; ++i)
{
div.AppendChild(HtmlNode.CreateNode("<div></div>"));
div.AppendChild(newline);
}
document.DocumentNode.AppendChild(div);
Console.WriteLine(document.DocumentNode.WriteTo());
Works great - the output:
<div>
<div></div>
<div></div>
</div>
Then thought, "no way....it can't be" - note the commented lines:
Dim document = New HtmlDocument()
Dim div = HtmlNode.CreateNode("<div></div>")
' this writes the literal string...
Dim newline = HtmlNode.CreateNode("\r\n")
' this works!
' Dim newline = HtmlNode.CreateNode(Environment.NewLine)
div.AppendChild(newline)
For i = 1 To 2
div.AppendChild(HtmlNode.CreateNode("<div></div>"))
div.AppendChild(newline)
Next
document.DocumentNode.AppendChild(div)
Console.WriteLine(document.DocumentNode.WriteTo())
Unfortunately it is so, and probably why the question you linked to was not marked answered - the output:
<div>\r\n<div></div>\r\n<div></div>\r\n</div>
Finally, instead of using the newline string as \r\n tried Environment.NewLine, which does work and outputs:
<div>
<div></div>
<div></div>
</div>
Works either way in C#.
Based on this answer you would need to add in a node that represents a Carriage Return (\r) and a Line Feed (\n):
Dim newLineNode As HtmlNode = HtmlNode.CreateNode("\r\n")
Based on your comment:
I tried this but it adds '\r\n' in my HTML, it's not going back to line.
You've already tried this and instead it prints the string literal "\r\n". I too have managed to replicate this issue.
Instead look at using <br> tag which is a line break:
Dim newLineNode As HtmlNode = HtmlNode.CreateNode("<br>")
Based on your example code, your code would look something like this:
Dim newLineNode As HtmlNode = HtmlNode.CreateNode("<br>")
For Each tr As HtmlNode In tables.Descendants("tr").ToArray
tr.AppendChild(div1)
tr.AppendChild(newLineNode)
tr.AppendChild(div2)
Next
However tables.Descendants("tr").ToArray did provide a compile error for me. As that's out of the scope of this question and you haven't raised it as an issue I'll make an assumption that it works for you.
I'm new to LibreOffice Basic. I'm trying to write a macro in LibreOffice Calc that will read the name of a noble House of Westeros from a cell (e.g. Stark), and output the Words of that House by looking it up on the relevant page on A Wiki of Ice and Fire. It should work like this:
Here is the pseudocode:
Read HouseName from column A
Open HtmlFile at "http://www.awoiaf.westeros.org/index.php/House_" & HouseName
Iterate through HtmlFile to find line which begins "<table class="infobox infobox-body"" // Finds the info box for the page.
Read Each Row in the table until Row begins Words
Read the contents of the next <td> tag, and return this as a string.
My problem is with the second line, I don't know how to read a HTML file. How should I do this in LibreOffice Basic?
There are two mainly issues with this.
1. Performance
Your UDF will need get the HTTP resource in every cell, in which it is stored.
2. HTML
Unfortunately there is no HTML parser in OpenOffice or LibreOffice. There is only a XML parser. Thats why we cannot parse HTML directly with the UDF.
This will work, but slow and not very universal:
Public Function FETCHHOUSE(sHouse as String) as String
sURL = "http://awoiaf.westeros.org/index.php/House_" & sHouse
oSimpleFileAccess = createUNOService ("com.sun.star.ucb.SimpleFileAccess")
oInpDataStream = createUNOService ("com.sun.star.io.TextInputStream")
on error goto falseHouseName
oInpDataStream.setInputStream(oSimpleFileAccess.openFileRead(sUrl))
on error goto 0
dim delimiters() as long
sContent = oInpDataStream.readString(delimiters(), false)
lStartPos = instr(1, sContent, "<table class=" & chr(34) & "infobox infobox-body" )
if lStartPos = 0 then
FETCHHOUSE = "no infobox on page"
exit function
end if
lEndPos = instr(lStartPos, sContent, "</table>")
sTable = mid(sContent, lStartPos, lEndPos-lStartPos + 8)
lStartPos = instr(1, sTable, "Words" )
if lStartPos = 0 then
FETCHHOUSE = "no Words on page"
exit function
end if
lEndPos = instr(lStartPos, sTable, "</tr>")
sRow = mid(sTable, lStartPos, lEndPos-lStartPos + 5)
oTextSearch = CreateUnoService("com.sun.star.util.TextSearch")
oOptions = CreateUnoStruct("com.sun.star.util.SearchOptions")
oOptions.algorithmType = com.sun.star.util.SearchAlgorithms.REGEXP
oOptions.searchString = "<td[^<]*>"
oTextSearch.setOptions(oOptions)
oFound = oTextSearch.searchForward(sRow, 0, Len(sRow))
If oFound.subRegExpressions = 0 then
FETCHHOUSE = "Words header but no Words content on page"
exit function
end if
lStartPos = oFound.endOffset(0) + 1
lEndPos = instr(lStartPos, sRow, "</td>")
sWords = mid(sRow, lStartPos, lEndPos-lStartPos)
FETCHHOUSE = sWords
exit function
falseHouseName:
FETCHHOUSE = "House name does not exist"
End Function
The better way would be, if you could get the needed informations from a Web API that would offered from the Wiki. You know the people behind the Wiki? If so, then you could place this there as a suggestion.
Greetings
Axel
I have a System.xml.xmlDocument() object which is rendered onto a web page by using XSL. I want to insert a 'linebreak` inside certain nodes in the XML object, so when the XML is rendered using XSLT there is an actual line break there. My Code to do this looks like this:
Dim parentNodes As System.Xml.XmlNodeList = objOutput.SelectNodes("//PARENT")
Dim currentParentValue As String = String.Empty
Dim resultParent As String = String.Empty
For Each par As System.Xml.XmlNode In parentNodes
currentParentValue = par.InnerText
Dim parArray As String() = currentParentValue.Split(";")
If parArray.Length > 2 Then
resultParent = String.Empty
Dim parCounter As Integer = 0
For Each Parent As String In parArray
parCounter = parCounter + 1
resultParent = resultParent + Parent + "; "
If (parCounter Mod 2) = 0 Then
resultParent = resultParent + "
"
End If
Next
End If
par.InnerText = resultParent
Next
And in XSL:
<td width="50%" nowrap="nowrap">
<xsl:value-of select="STUDENT_DETAILS/PARENT"/>
</td>
However, it looks like xmlDocument is automatically escaping the next line character, so it just appears as text on the page, can anyone tell how to fix this?
If you change
<td width="50%" nowrap="nowrap">
<xsl:value-of select="STUDENT_DETAILS/PARENT"/>
</td>
to
<td width="50%" nowrap="nowrap">
<pre>
<xsl:value-of select="STUDENT_DETAILS/PARENT"/>
</pre>
</td>
the browser will render line breaks.
you can just simple append "<'br\>" next to your nodes, that will insert the linebreak between yours two nodes.
Notes:
please remove the ' before br.
You problem resolves around this line....
resultParent = resultParent + "
"
Now, you are probably trying to output your XML like this:
<PARENT>George Aaron
Susan Lee Aaron
Richard Elliot Aaron
</PARENT>
However, this escaped
entity is only relevant if the document has yet to be parsed. If it were a text document, that gets subsequent read and parsed into an XML document, then the entities would be handled as expected. But you are working with an XML document that has already been parsed. Therefore, when you do resultParent = resultParent + "
" it is actually going to insert a string of five characters into an existing text node, and because & is a special character, it gets escaped.
Now, what you can simply do is this...
resultParent = resultParent + chr(10)
But ultimately this will prove fruitless because HTML doesn't recognise line-break characters, so you would have to write your XSLT to replace the line break with a <br /> element.
If you wanted to do this in your VB code though, you could create new br elements yourself, and insert them
For Each par As System.Xml.XmlNode In parentNodes
currentParentValue = par.InnerText
par.InnerText = String.Empty
Dim parArray As String() = currentParentValue.Split(";")
For Each Parent As String In parArray
If Parent.Length > 0 Then
Dim person As XmlText = objOutput.CreateTextNode(Parent)
par.AppendChild(person)
par.AppendChild(objOutput.CreateElement("br"))
End If
Next
Next
So, this takes the PARENT node, clears it down, then adds a text node, and new br element for each parent. The output would then be like so, which would be much easier to output as HTML using XSLT
<PARENT>George Aaron<br />Susan Lee Aaron<br />Richard Elliot Aaron<br /></PARENT>
(It shouldn't be too hard to add the br after every second parent if required).
However, if may not necessarily be a good idea to put "presentational" information in a XML file. Suppose you later had to transform the XML into a different format? An alternate approach would be separate each parent into their own element.
For Each par As System.Xml.XmlNode In parentNodes
currentParentValue = par.InnerText
par.InnerText = String.Empty
Dim parArray As String() = currentParentValue.Split(";")
For Each Parent As String In parArray
If Parent.Length > 0 Then
Dim person As XmlElement = objOutput.CreateElement("PERSON")
person.InnerText = Parent.Trim()
par.AppendChild(person)
End If
Next
Next
This would output something like this..
<PARENT>
<PERSON>George Aaron</PERSON>
<PERSON>Susan Lee Aaron</PERSON>
<PERSON>Richard Elliot Aaron</PERSON>
<PERSON>Albert Smith</PERSON>
</PARENT>
Displaying this as HTML would also be straight-forward
Hint: To display in groups of two, your XSLT may look something like this....
<xsl:for-each select="PERSON[postion() mod 2 = 1]">
<xsl:value-of select=".">;
<xsl:value-of select="following-sibling::PERSON[1]" />
<br />
</xsl:for-each>
Is there a way in arcobjects to get a unique id for a layer? If you do a search by layer name there could be possible duplicates.
If there isn't a property is there a way to generate an id?
I tried using the GetHash() but that didn't stay consistent.
There is an ArcObjects Interface present for setting or getting an Id for a layer.
You should look at ILayerDescriptor:ID,
http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriCarto/ILayerDescriptor_ID.htm
Here is a VBA Snippet which shows how it can be used:
Public Sub layerInfo()
Dim app As IApplication '
Set app = Application
Dim mxDoc As IMxDocument
Set mxDoc = app.Document
Dim myMap As IMap
Set myMap = mxDoc.ActiveView
Dim mapServer As IMxdServer
Set mapServer = New MxdServer
'''Point to your .mxd...
mapServer.Start ("D:\Test.mxd")
Dim myArray As IArray
Set myArray = mapServer.LayerDescriptors(myMap.Name)
MsgBox myArray.Count
Dim x As ILayerDescriptor
Dim intX As Integer
intX = 0
For intX = 0 To myArray.Count - 1
Set x = myArray.Element(intX)
MsgBox x.ID
MsgBox x.Name
Next
End Sub
It isn't pretty, but in the past I've appended a guid in the layer description. Something like this:
<LAYER guid='a9843c88-3caa-4953-ad96-ca9990b410e9' revision='1' />
I've got a DLL floating around that would slam these xml frags into each layer of an MXD (with enough cr/lf in front to scroll the xml fragment out of the layer description in ArcMap Layer Prop dialog) .
There's a help file in the 7z file (documentation is sparse because I'm doing other things):
http://code.google.com/p/umbriel/downloads/list
I like the idea of using a GUID. This can then be stored in the ModelName property which is a tool for developers of custom objects to use to guarantee the names of objects independent of the true name or alias name.
There are more details and sample code at http://geographika.co.uk/?p=58
Easy. A side effect of using COM and because how the vtables are laid out, is that you can use the memory address of the layer itself as your unique identifier. Inside the implementation of many ESRI GeoDatabase and Carto code itself, this trick is being used all over the place.