I've got an ASP.NET (VB) user control that I am going to be loading an XML document into. This document could have one of two possible root node names. I have 3 possible xsl files that will need to be prepended in the xml file through the .NET. Adding the xsl reference is not a problem but I'm running into an issue determining WHICH xsl file to add.
The first thing I need to determine, after I load the XML document is what is the root node. It can be either
<Document xmlns="urn:hl7-org:v3">
<templateId root="usetemplate_1" />
or
<Document xmlns="urn:hl7-org:v3">
<templateId root="usetemplate_2" />
or
<Record xmlns="urn:astm-org:REC">
<objectId>useobjectid</objectId>
Take note that the first two have one namespace and the third has a different namespace (not sure how to reconcile that in my brain yet either).
Each one of these options has a different xsl file to be referenced. I've got similar code in another place that I can reuse, as I said, it's just a matter of determining the right file to reference. I've got this so far:
Dim m_xmld As XmlDocument
Dim m_nodelist As XmlNodeList
Dim m_node As XmlNode
m_xmld = New XmlDocument
m_xmld.Load(Me.XMLFileName)
Dim nsmgr As New XmlNamespaceManager(m_xmld.NameTable)
nsmgr.AddNamespace("x", "urn:hl7-org:v3")
m_nodelist = m_xmld.SelectNodes("x:Document/", nsmgr)
For Each m_node In m_nodelist
Next
I'm just at a loss as to what to put between the for each/next lines to determine which one from the options above or if that is even necessary and can be done more efficiently a different way.
This should work:
Dim m_xmld As New XmlDocument()
m_xmld.Load(Me.XMLFileName)
Dim nsmgr As New XmlNamespaceManager(m_xmld.NameTable)
nsmgr.AddNamespace("h", "urn:hl7-org:v3")
nsmgr.AddNamespace("a", "urn:astm-org:REC")
If m_xmld.SelectSingleNode("/h:Document/h:templateId[#root='usetemplate_1']", nsmgr) IsNot Nothing Then
' Use xsl A
ElseIf m_xmld.SelectSingleNode("/h:Document/h:templateId[#root='usetemplate_2']", nsmgr) IsNot Nothing Then
' Use xsl B
ElseIf m_xmld.SelectSingleNode("/a:Record/a:objectId", nsmgr) IsNot Nothing Then
' Use xsl C
End If
Or, if a Select Case makes more sense, you can get the value of the attribute, like this:
Dim root As XmlNode = doc.SelectSingleNode("/h:Document/h:templateId/#root", nsmgr)
If root IsNot Nothing Then
Select Case root.InnerText
Case "usetemplate_1"
' Use xsl A
Case "usetemplate_2"
' Use xsl B
End Select
ElseIf doc.SelectSingleNode("/a:Record/a:objectId", nsmgr) IsNot Nothing Then
' Use xsl C
End If
You can add as many namespaces as you want to the namespace manager. Even if the namespaces don't actually get used anywhere in the XML document, that won't be a problem. So, as you can see, you can add both potential namespaces at the same time.
Related
Currently I'm working on a script that will go through a given folder and search for all files with a specific extension. It will then print out the name and sum the file size. I believe i have most of the issues sorted out, so this question isn't about how to do this.
Instead, I want to know what would be the best practice for using FileSystemObject streams in a recursive function. Should I be using a single stream for all calls (either global, or passed), or should I be creating a new stream for each recursive step?
For extra fun, I'm planning on having this access multiple PCs, and over UNC path. And yes, I expect there's a better way of doing this, but I'm relatively new with VBS.
Current code:
'Recursive Function handles search of files in folder and subfolders.
Function UNCSearchFolder(strUNCRootPath, strUNCNextFolder)
Dim objFSOUNCSearchFolder, objFSOUNCSearchFolder2, colFolderList, objFolder, strCurrentFolder, strSubFolder
'Get list of Subfolders in folder: <Rootpath>\<Nextfolder>
strCurrentFolder = strUNCRootPath & "\" & strUNCNextFolder & "\"
Set objFSOUNCSearchFolder = CreateObject("Scripting.FileSystemObject")
Set objFSOUNCSearchFolder2 = objFSOUNCSearchFolder.GetFolder(strCurrentFolder)
Set colFolderList = objFSOUNCSearchFolder2.SubFolders
'Subfolder dive
For Each objFolder in colFolderList
strSubFolder = objFolder.name
'REMOVE THIS ECHO LATER
wscript.echo strSubFolder
UNCSearchFolder(strCurrentFolder, strSubFolder)
Next
'Search for files here
'GC on File Streams
Set objFSOUNCSearchFolder2 = Nothing
Set objFSOUNCSearchFolder = Nothing
End Function
So, should one filestream be used for all accesses or should each step use one separately? Is it a moot point? Will this cause multiple connections to each system or should it only use one? Basically I want the script to work without disrupting users, or causing weird responses (ie, running out of active connections). The script will only be used a couple times for an audit we're doing, but may eventually be repurposed for future audits.
Let me know what you think. Thanks for any help,
If you choose to set a reference to FSO inside your function,
then in each recursion will be used new FSO object.
Using single FSO object (either global, or passed) is quite enough.
At least I don't know any benefit of using multiple FSO instances.
[EDIT] I appreciate #AnsgarWiechers comment, and to make the code ready for re-using, while kept the FSO out of the function, we can wrap our function in a class.
With New FileInfo
WScript.Echo .FileSize("C:\temp", "txt", True)
End With
Class FileInfo
Private m_oFSO
Public Function FileSize(sRootDir, sExtension, bRecursive)
Dim oFolder, oFile, sFExt
sFExt = LCase(sExtension)
Set oFolder = m_oFSO.GetFolder(sRootDir)
For Each oFile In oFolder.Files
If LCase(m_oFSO.GetExtensionName(oFile.Name)) = sFExt Then
FileSize = FileSize + oFile.Size
End If
Next
If bRecursive Then
Dim oSubFolder
For Each oSubFolder In oFolder.SubFolders
FileSize = FileSize + FileSize(oSubFolder, sExtension, True)
Next
End If
End Function
Private Sub Class_Initialize
Set m_oFSO = CreateObject("Scripting.FileSystemObject")
End Sub
Private Sub Class_Terminate
Set m_oFSO = Nothing
End Sub
End Class
I have an XML file and I have a function to which this XML is passed as string. I have loaded this string in Xmldocument and I need to insert few children nodes under one node. This function returns the modified XML string. I want that whenever this function is called, new child nodes are added, currently, It adds child nodes once.
Do I need to overwrite the XML file ? If so, How can I replace the node with new node ( with new child nodes added) so that It has child nodes added before the function is called again ?
My code looks something like this:
Dim doc As New XmlDocument
doc.LoadXml(applicationXml)
Dim parentNode As XmlNode = doc.GetElementsByTagName("prList").Item(0)
Dim newElement As XmlNode = doc.CreateNode(XmlNodeType.Element, "gate.util.persistence.LanguageAnalyserPersistence", Nothing)
Dim runtimeParamsElement As XmlNode = doc.CreateNode(XmlNodeType.Element, "runtimeParams", Nothing)
Dim xa As XmlAttribute = doc.CreateAttribute("class")
xa.Value = "gate.util.persistence.MapPersistence"
runtimeParamsElement.Attributes.Append(xa)
localMapElement = doc.CreateNode(XmlNodeType.Element, "localMap", Nothing)
featuresElement.AppendChild(localMapElement)
newElement.AppendChild(featuresElement)
Return doc.InnerXml
You are not showing enough code to determine the exact problem. Which node are you trying to replace? Where did featuresElement come from? Here are a few answers:
Yes you need to replace the whole file using the new document.
You don't need to replace the node with a new node. Just pass the target node to your function. The function should call appendChild() on the parent node to add a child.
When you write the document to file, example, it will have the new nodes.
I am trying to use the value of <Directory> in my following piece of code:
Public Function GetFile() As String
Dim di As New DirectoryInfo(< Directory >)
Dim files As FileSystemInfo() = di.GetFileSystemInfos()
Dim newestFile = files.OrderByDescending(Function(f) f.CreationTime).First
Return newestFile.FullName
End Function
Is there any way i can call the value stored in the xml file in my code?
Andy's answer is good, but in VB it's even easier.
Dim xmlDoc As XDocument
Dim dir as String
xmlDoc = XDocument.Load("XMLFile1.xml")
dir = xmlDoc.<ServerList>.<Server>.<Directory>.First().Value;
Or even easier if the XML file will never have more than one <Directory> element that you care about:
dir = xmlDoc...<Directory>.First().Value;
To answer your comment on Andy's answer:
dir = (From server as XElement in xmlDoc...<Server>
Where server.<ServerName>.First().Value = requiredServer
Select server.<Directory>.First().Value)(0);
As you are clearly familiar with Linq, you can operate on the Xml using System.Xml.Linq.
Apologies, this is in c#.
var xDoc = XDocument.Load("XMLFile1.xml");
var dir = xDoc.Element("ServerList").Elements("Server").Elements("Directory").First().Value;
If you have the Xml stored in a string replace XDocument.Load with XDocument.Parse.
Obviously you'll have to defend against parse errors, file missing and schema inconsistencies in your production code.
You can use this http://support.microsoft.com/kb/301225
I have a trouble on appending new node to xmldocument (created in the memory). I have select the root node with the XmlDocument.SelectSingleNode() method, it work sometimes and in the other time it will give me "System.InvalidOperationException: This document already has a 'DocumentElement' node." error. More information, this xml document is multi level xml document.
By the way, when i try it with unit test it work fine (always), when i implement it in ASP.NET 3.5, it become weird, work sometimes and fail sometimes. Any idea, why this can help? All advise and suggestion are welcome.
Thanks.
You can select the root node of the XmlDocument using the Property "DocumentElement". Or i think you can use the Property "FirstChild" (untested).
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
XmlElement rootNode = doc.DocumentElement;
This worked for me.
xmlOriginFile = New XmlDocument()
xmlTargetFile = New XmlDocument()
xmlOriginFile.Load(readFile) //readFile is a string that hold path to xml document
xmlTargetFile.Load(writeFile) //writeFile is a string that hold path to xml document
Dim fileNav As XPathNavigator = xmlOriginFile.CreateNavigator()
Dim fileItr As XPathNodeIterator = fileNav.Select("//data")
Dim addToDestNodes As List(Of XmlNode) = New List(Of XmlNode)
While (fileItr.MoveNext())
Dim addNode As XmlNode = CType(fileItr.Current, IHasXmlNode).GetNode()
addToDestNodes.Add(addNode)
End While //loop thru nodes
If addToDestNodes.Count > 0 Then
For Each addedNode As XmlNode In addToDestNodes
Dim addTargetNode As XmlNode = xmlTargetFile.ImportNode(addedNode, True)
xmlTargetFile.DocumentElement.AppendChild(addTargetNode)
Next
End If
xmlTargetFile.Save(xmlTarget) //xmlTarget is a string that hold path to xml document
XML has a root element and you have to add new element within this root element.
XmlElement eleParent = docDestn.CreateElement("EleParent");
XmlElement eleChild = docDestn.CreateElement("Item");
eleParent.AppendChild(eleChild);
XMLNode rootNode= xmlDoc.SelectSingleNode("RootEle");
rootNode.AppendChild(eleParent);
Plps. refer the link for detail: http://navinpandit.blogspot.in/2016/12/exception-this-document-already-has.html
How can I transform a website to be able to handle multi language (example : english, french, spanish)?
I do not like the resource file because I feel limited and it's pretty long to build the list. Do you have any suggestion?
Update
For the moment the best way we found is to use an XML file and with some Xpath et get values.
Implicit localization (on the Visual Studio - Tools menu - Generate Local Resources) is about as easy as it can be. Write your pages in your default language, pick the menu option, and your resource files are created and can be sent to someone to translate.
The resx file is just xml, so if the translation company wants you can transform it into (and out of) spreadsheets easily.
Using a databases instead of resx as your backing store is not difficult. Rick Strahl has a good explanation and example code for a database-driven localization provider here - there's a nice built in localization editor too with interface to Google translations and Babelfish.
We store resources for multilingual sites in a database. We've created a couple of tools to make it easy to create and access these. There's a custom ExpressionBuilder that allows us to use this syntax:
<asp:linkbutton runat='server' text='<%$ LanguageStrings:ClickMe%>' />
And a custom label that contains the default text, and adds a row to the database if there's not already one.
<r:languagelabel runat="server" name="AboutUs">About Us</r:languagelabel>
The table containing the strings has one column per language. This makes it very easy to create the site in English (or whatever the default language is), then hand off the table (which populates itself) to a translator. It's also very easy to see what languages you need to have stuff translated for. With resources, every time you need to add a new string, you have to stop what you're doing, and then go to the resource file for each language and add the resource.
Here's the code for the language label:
''' <summary>
''' Retrieves a language-specific string.
''' </summary>
Public Class LanguageLabel
Inherits Label
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private Sub Populate()
If Len(Me.Name) > 0 Then
Dim LanguageString As String = GetLanguageString(Me.Name, Me.Text)
If Len(LanguageString) > 0 Then Me.Text = LanguageString
End If
End Sub
Private Sub LanguageLabel_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
Populate()
End Sub
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
' By default a label wraps the text in a <span>, which we don't want in some situations
writer.Write(Me.Text)
End Sub
End Class
and the utility function:
Public Function GetLanguageString(ByVal Name As String, Optional ByVal DefaultText As String = "") As String
Dim DefaultLanguage As Language = Languages.GetById(1)
Name = StripPunctuation(Name).Trim.Replace(" ", "") ' Remove punctuation, spaces from name
Dim SelectSql As String = String.Format("Select {0},{1} from LanguageStrings where Name=#Name", Languages.CurrentLanguage.Code, DefaultLanguage.Code)
Dim LanguageStringTable As DataTable = ExecuteDataset(cs, CommandType.Text, SelectSql, New SqlParameter("#Name", Name)).Tables(0)
If LanguageStringTable IsNot Nothing AndAlso LanguageStringTable.Rows.Count > 0 Then
Dim LanguageText As String = LanguageStringTable.Rows(0)(Languages.CurrentLanguage.Code).ToString
Dim DefaultLanguageText As String = LanguageStringTable.Rows(0)(DefaultLanguage.Code).ToString
If Len(LanguageText) > 0 Then
' We have a string in this language
Return LanguageText
Else
' Nothing in this language - return default language value
Return DefaultLanguageText
End If
Else
' No record with this name - create a dummy one
If DefaultText = "" Then DefaultText = Name
Dim InsertSql As String = String.Format("Insert into LanguageStrings (Name, {0}) values (#Name, #Text)", DefaultLanguage.Code)
ExecuteNonQuery(cs, CommandType.Text, InsertSql, New SqlParameter("#Name", Name), New SqlParameter("#Text", DefaultText))
Return Name
End If
End Function
Resource files are the way to go. We ship our product in 12 languages. We pull all strings out into resource files and ship them to a translation company. It's a pain at times, but that is the defacto way to do it.
It also gets fun when 4-letter English words get translated into 17-letter phrases and you have to tweak your UI.
How late in the design process are you? If not too late, and if the budget allows, consider porting to a multi-lingual CMS like Ektron CMS300.net (which has built-in translation tools). If not, then you've got a huge task ahead of you.
Another solution I am using is to create the language folders which contain the aspx pages containing all the required text in that particular language.
The only problem here is how can you inject as little code as possible into those replicating pages. I am using a controller pattern here to do this, and then a object data source to get the data and bind it to the controls in all pages.
In this way I have achieved the goal of getting rid of the resource files and I can keep the code behind in one place without replicating it (unless necessary).
Edit: I would recommend a good CMS framework as well.
One of the web apps I develop has this NLS requirement too.
I found that there are at least 3 locations where you have localized texts:
user interface
database tables ("catalogs" or whatever you want to call them)
backend code (services etc)
My solution has one table for the pages, tables, etc ("Container"), one table for each item in that container (e.g. labels, buttons by ID, record identifiers), and one table for the translated items (plus language identifier).
A translation application helps me keep the translations up-to-date, and exports all translations in XML.
The product ships with translations, but customers can adjust the translations, changes taking effect immediately.
Sample code i have done using resource file
add global.asax
void Application_BeginRequest(Object sender, EventArgs e)
{
// Code that runs on application startup
HttpCookie cookie = HttpContext.Current.Request.Cookies["CultureInfo"];
if (cookie != null && cookie.Value != null)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(cookie.Value);
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cookie.Value);
}
else
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en");
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en");
}
}
http://satindersinght.blogspot.in/2012/06/create-website-for-multilanguage.html
http://satindersinght.wordpress.com/2012/06/14/create-website-for-multilanguage-support/