Tuesday, June 20, 2017

convert csv to xml

Hello,

Since i have nothing to do in my free time, i answered a post in expert exchange to convert a CSV to XML

Here is my code, it's a VBS script

It does not support everything the CSV can contain

The input CSV was sometimes with double quotes, sometimes not
Sometimes there was carriage return in a field, sometimes 2

So i support: double quotes in CSV
and up to two carriage return in a field
You can change it easily to support more carriage returns in a field

The files must all be in same folder

The log (file starting with ZZZ in same folder) will tell you if something went wrong


'=== CSV to xml
'=== by sergefournier@hotmail.com 2017-06-20

'=== save thsi script as: csv2xml.VBS
'=== input file must be sample1.csv in the same directory as the script (same folder)
'=== outputfile will be sampleout1.xml
'=== zzz_troubleshooting.txt will tell you what was not processed (lines)

'=== support 2 carriage return in a field, but not 3 :P

Set objFSO    = CreateObject("Scripting.FileSystemObject")

'=== actual drive, actual directory, and "\"
thepath=WScript.ScriptFullName
p = instrRev(thepath,"\")
basedir  = left(thepath,p)
filnam = right(thepath,len(thepath)-p)

logall = 1

if logall=1 then
   '=== debug log
   file02 = basedir & "zzz_troubleshooting.txt"
   on error resume next
   Set Fil02 = objFSo.OpenTextFile(file02, 2, true)
   on error goto 0
end if

if logall=1 then
   fil02.WriteLine date & " " & time & " START"
end if


'=== input (txt file, not using ODBC txt driver)
filename01 = basedir & "sample1.csv"
Set File01 = objFSo.OpenTextFile(filename01, 1, true)


'=== read filed names (1st line)


if file01.AtEndOfStream <> true then
line01 = file01.readline

'=== array with all field names
arr01 = split(line01,",")
for i = 0 to ubound(arr01)
arr01(i) = trim(arr01(i)) '=== remove spaces
arr01(i) = replace(arr01(i), " ", "")
arr01(i) = replace(arr01(i), "/", "")
fil02.WriteLine date & " " & time & " fieldname: " & arr01(i)
next

'=== spaces in xml name are not good


'=== output xml
'=== create xml
Set xmlDoc = CreateObject("Microsoft.XMLDOM")
Set objRoot = xmlDoc.createElement("data")
xmlDoc.appendChild objRoot

Set objIntro = xmlDoc.createProcessingInstruction("xml","version='1.0'")
xmlDoc.insertBefore objIntro,xmlDoc.childNodes(0)

xmlDoc.Save basedir & "sampleout1.xml"


'=== add a record
Set xmlDoc = CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load(basedir & "sampleout1.xml")

Set objRoot = xmlDoc.documentElement
linecount01 = 0

Do While file01.AtEndOfStream <> True
Set objRecord = xmlDoc.createElement("client")
objRoot.appendChild objRecord

line01 = file01.readline
'=== split value in CSV
arr02 = split(line01,",")

x = 0 '=== number of elements in new array to merge elements with double quotes
redim arr03(x)

'2017-06-20 21:43:50 DATA before: 10400,Pep,Pepmiller,Pep.Pepmiller@RalphLauren.com,"2800 Routh Street, Suite 260",Dallas,TX,75201
'2017-06-20 21:43:50 DATA after : ,10400,Pep,Pepmiller,Pep.Pepmiller@RalphLauren.com,2800 Routh Street"2800 Routh Street Suite 260,TX,75201
if ubound(arr02) <> 0 then
if ubound(arr01) <> ubound(arr02) then

'=== do we have a double quote and a carriage return before we meet the closing double quote
doublequoteanomaly01 = 1
for i = 0 to ubound(arr02)
'=== search for a closing double quote, in case a carriage return separate the line
if right(arr02(i),1) = """" then
'=== found a closing double quote, no anomaly
doublequoteanomaly01 = 0
end if
next

if doublequoteanomaly01 = 1 then
fil02.WriteLine date & " " & time & " --- found double quote alone, will add next line: " & linecount01
'=== we have only one double quote in the line, wich mean there is a carriage return after the double quote
'=== we have to read the next line and merge both lines
if file01.AtEndOfStream <> true then
line03 = file01.readline
linecount01 = linecount01 + 1
'=== data was on two line, we add a space instead of a carriage return
'=== xml will support a carriage return special code
line01 = line01 & " " & line03

if instr(line03, """") = 0 then
fil02.WriteLine date & " " & time & " --- final double quote not in line, reading another line: " & linecount01
if file01.AtEndOfStream <> true then
line03 = file01.readline
linecount01 = linecount01 + 1
line01 = line01 & " " & line03
end if
end if

arr02 = split(line01,",")
end if
end if



if ubound(arr01) <> ubound(arr02) then
'=== a data might have quote to support a comma inside of it
'fil02.WriteLine date & " " & time & " DATA before: " & line01

line02 = ""
for i=0 to ubound(arr02)
if i < ubound(arr02) then
line02 = line02 & arr02(i) & ","
else
line02 = line02 & arr02(i)
end if
next

'fil02.WriteLine date & " " & time & " DATA befor2: " & line02

skip01 = 0
for i = 0 to ubound(arr02)

newdata01 = arr02(i)


if left(arr02(i),1) = """" then
'=== double quote found
newdata01 = right(newdata01,len(Arr02(i))-1) & ","
i3 = i + 1
for i2 = i3 to ubound(arr02)
if right(arr02(i2),1) = """" then
newdata01 = newdata01 & left(arr02(i2),len(arr02(i2))-1)
i = i + 1
'=== end double quote found, we exit the for i2
exit for
else
newdata01 = newdata01 & arr02(i2)
i = i + 1
end if
next
end if


redim preserve arr03(x)
arr03(x) = newdata01
x = x + 1

next
arr02 = arr03

line02 = ""
for i=0 to ubound(arr03)
if i < ubound(arr03) then
line02 = line02 & arr03(i) & ","
else
line02 = line02 & arr03(i)
end if
next
'fil02.WriteLine date & " " & time & " DATA after : " & line02
else
'=== reading second line was enough for element to fit in numbers (data number = fields numbers)
'=== there was no comma, justye a carriage return
'=== just remove double quotes in the splitted elements
for i=0 to ubound(arr02)
arr02(i) = replace(arr02(i),"""","")
next

end if

end if

if ubound(arr01) = ubound(arr02) then

for i = 0 to ubound(arr01)

Set objFieldValue = xmlDoc.createElement(arr01(i))
'objfieldvalue.SetAttribute "displaynamefra", "Numéro"
objFieldValue.Text = arr02(i)
objRecord.appendChild objFieldValue
next
else
fil02.WriteLine date & " " & time & " ERROR line: " & linecount01
fil02.WriteLine date & " " & time & " the number of columns (fields) does not correspond to the number of data"
fil02.WriteLine date & " " & time & " ubound arr01: " & ubound(arr01)
fil02.WriteLine date & " " & time & " ubound arr02: " & ubound(arr02)
fil02.WriteLine date & " " & time & " data: " & line01
end if
else
fil02.WriteLine date & " " & time & " ERROR line empty: " & line02
end if
linecount01 = linecount01 + 1

loop

xmlDoc.Save basedir & "sampleout1.xml"
else
msg01 = "ERROR input file is empty"
msgbox(msg01)
end if


file01.close

Saturday, June 3, 2017

Virtualbox change UUID (GUID)

Hello,

I am lazy
So when i read that to change a virtualbox UUID (GUID) you had to:
1. run a command in virtualbox folder to change VDI file's UUID
2. change the machine UUID in the .VBOX file
3. change the HardDisk UUID to reflect the VDI UUID
4. change the Image UUID to reflect the VDI UUID

I had to do something automating this process

This small script, will:
find location of virtualbox folder (5.x)
Run VBoxManage.exe to change VDI file UUID (guid)
EDIT .VBOX (that is a XML file) and change the 3 id: machine, harddisk (same as VDI) and image (same as VDI)

Put the script in the same folder as the VBOX and VDI file
(it will browse the folder to find a .VDI file and a .VBOX file, ONLY one of each)

here it is:

------------------------ vbox_set_new_id.vbs -----------------------

'=== using vboxmanage, this script will assign a new UID to a virtualbox machine, hard disk and image

Set objshe = WScript.CreateObject("WScript.Shell")
Set objFSO = wscript.CreateObject("Scripting.FileSystemObject")
Set objNet    = CreateObject("WScript.Network")

Const hkcr = &H80000000 'HKEY_CLASSES_ROOT
Const HKCU = &H80000001 'HKEY_CURRENT_USER
Const hklm = &H80000002 'HKEY_LOCAL_MACHINE
Const hku  = &H80000003 'HKEY_USERS
Const hkcc = &H80000005 'HKEY_CURRENT_CONFIG

'=== actual drive, actual directory, and "\"
thepath=WScript.ScriptFullName
p = instrRev(thepath,"\")
basedir  = left(thepath,p)
filnam = right(thepath,len(thepath)-p)

'=== restart the script in 32 bits if we are on a 64 bits system (if wa want that architecture)
'=== (most of the time, ODBC is available only in 32 bits)
architecturewanted01 = 64

if architecturewanted01 = 32 then
a64 = windir & "\syswow64\wscript.exe"

if objFSO.fileEXISTS(a64) and instr(lcase(wscript.fullname),"syswow64")=0 then
  '=== 64 bits system detected, restart in 32 bits
  a = """" & a64 & """ """ & basedir & filnam & """"
  objshe.Run a,0, false
  wscript.quit
end if
end if

logall = 1

if logall=1 then
   '=== debug log
   file02 = basedir & "zzz_troubleshooting.txt"
   on error resume next
   Set Fil02 = objFSo.OpenTextFile(file02, 2, true)
   on error goto 0
end if

'=== virtualbox assign new UID to a VM to be able to copy it
'=== value
'=== chek if the file already exist
Set objFol01=objFSO.GetFolder(basedir)'=== dir
Set objfol02=objFol01.files '=== files


regpath01 = "SOFTWARE\Oracle\VirtualBox"
virtualboxpath01 = regrea(0, hklm, regpath01, "installdir")
'pathcom01 = "C:\Program Files\Oracle\VirtualBox" '=== manual path for virtualbox commandline

if not isnull(useoff) then
'=== get all filename in this folder, put them in array
x=0
redim ara01(0)
dimnum=1
For Each objFil in objFol02
filnam=objfil.name
filnam=lcase(filnam)
if right(filnam,4)=".vdi" then
redim preserve ara01(x)
ara01(x)=filnam
x=x+1
end if
next
if x = 1 then
'=== found 1 .VDI file
'com01 = virtualboxpath01 & "VBoxManage.exe internalcommands sethduuid ""/home/user/VirtualBox VMs/drupal/drupal.vhd"""

''''''''''''''''''''''''''''''
' VDI guid (diskfile)
''''''''''''''''''''''''''''''
Set TypeLib = CreateObject("Scriptlet.TypeLib")
vdiguid01 = TypeLib.Guid
vdiguid01 = left(vdiguid01, len(vdiguid01)-2) '=== remove two NULL at end
fil02.writeline "vbox machine future vdiguid01: " & vboxguid01

com01 = """" & virtualboxpath01 & "VBoxManage.exe"" internalcommands sethduuid """ & basedir & ara01(0) & """ " & vdiguid01
fil02.writeline com01
'msgbox(com01)
'=== set new GUID for the VDI (disk)
objshe.Run com01, 0, false

x=0
redim ara01(0)
dimnum=1
For Each objFil in objFol02
filnam=objfil.name
filnam=lcase(filnam)
if right(filnam,5)=".vbox" then
redim preserve ara01(x)
ara01(x)=filnam
x=x+1
end if
next

if x = 1 then
'=== xml filename for .VBOX file
filename01 = ara01(0)

'''''''''''''''''''''''''''''''''''''''''''''''''
' machine GUID
'''''''''''''''''''''''''''''''''''''''''''''''''
Set TypeLib = CreateObject("Scriptlet.TypeLib")
vboxguid01 = TypeLib.Guid
vboxguid01 = left(vboxguid01, len(vboxguid01)-2) '=== remove two NULL at end
fil02.writeline "vbox machine future vboxguid01: " & vboxguid01
fil02.writeline "vbox xml filename: " & filename01
'=== change VBOX xml file

Set xmlDoc = CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load(filename01)
'Set objRoot = xmlDoc.documentElement

'=== root node level 0
'Set Node00 = objroot.SelectSingleNode("/*")

'=== references
'https://msdn.microsoft.com/en-us/library/system.xml.xmlelement(v=vs.100).aspx

'Set colNodes01 = xmlDoc.selectNodes("/*")
'<VirtualBox xmlns="http://www.virtualbox.org/" version="1.15-windows">
'  <Machine uuid="{bee53fbc-651d-4ddb-9e48-10923f5a8f6e}" name="win7sp1" OSType="Windows7_64" snapshotFolder="Snapshots" lastStateChange="2017-06-04T00:22:09Z">
'    <MediaRegistry>
'      <HardDisks>
'        <HardDisk uuid="{76394b53-4740-4ba0-a422-017eaaaaa020}"
'    <StorageControllers>
'      <StorageController name="SATA" type="AHCI" PortCount="3" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" 'IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
'        <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
'          <Image uuid="{76394b53-4740-4ba0-a422-017eaaaaa020}"/>

'open the .vbox file in a text editor
'replace the UUID found in Machine uuid="{...}" with the UUID you got when you ran sethduuid the first time
'replace the UUID found in HardDisk uuid="{...}" and in Image uuid="{}" (towards the end) with the UUID you got when you ran sethduuid the second time
'=== machine GUID or UUID replacement
xmlpath01 = "/VirtualBox/Machine"
Set colNodes01 = xmlDoc.selectNodes(xmlpath01)
node = 1
For Each objNode in colNodes01
fil02.writeline "vbox machine guid nodes browsing..."
'=== get machine GUID (or UUID)
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (before) " & value01
objNode.SetAttribute "uuid", vboxguid01
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (after ) " & value01
next
Set colNodes01 = nothing
'''''''''''''''''''''''''''''''''
' hd and image GUID
'''''''''''''''''''''''''''''''''
'Set TypeLib = CreateObject("Scriptlet.TypeLib")
'hdimageguid01 = TypeLib.Guid
'hdimageguid01 = left(hdimageguid01, len(hdimageguid01)-2) '=== remove two NULL at end
hdimageguid01 = vdiguid01
fil02.writeline "vbox machine future hdimageguid01: " & hdimageguid01

'=== harddisk GUID
xmlpath01 = "/VirtualBox/Machine/MediaRegistry/HardDisks/HardDisk"
Set colNodes01 = xmlDoc.selectNodes(xmlpath01)
node = 1
For Each objNode in colNodes01
fil02.writeline "vbox harddisk guid nodes browsing..."
'=== get harddisk GUID (or UUID)
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (before) " & value01
objNode.SetAttribute "uuid", hdimageguid01
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (after ) " & value01
next
Set colNodes01 = nothing

'=== image GUID
xmlpath01 = "/VirtualBox/Machine/StorageControllers/StorageController/AttachedDevice/Image"
Set colNodes01 = xmlDoc.selectNodes(xmlpath01)
node = 1
For Each objNode in colNodes01
fil02.writeline "vbox harddisk guid nodes browsing..."
'=== get Image GUID (or UUID)
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (before) " & value01
objNode.SetAttribute "uuid", hdimageguid01
value01 = objNode.getattribute("uuid")
fil02.writeline xmlpath01 & ": (after ) " & value01
next
Set colNodes01 = nothing

xmlDoc.Save basedir & filename01

else
msg01 = "ERROR found too many or too few VBOX files"
msg01 = msg01 & "Number of vm files found: " & x
msgbox(msg01)
end if
ELSE
msg01 = "ERROR found too many or too few VDI files"
msg01 = msg01 & "Number of vm files found: " & x
msgbox(msg01)
END IF
else
'=== virtualbox not found
msg01 = "ERROR virtualbox register key installdir not found"
msg01 = msg01 & "reg key wanted: HKEY_LOCAL_MACHINE\" & regpath01
msgbox(msg01)
end if

if logall=1 then
   fil02.WriteLine date & " " & time & " END"
end if
if logall = 1 then
   fil02.close
end if

'============================================================================

'=== lis le registre en mode 32 bits, si rien, lis en 64 bits
function regrea(r2egrea_mode, r2egrea_clef01, r2egrea_clef02, r2egrea_clef03)
   'Inparams.Hdefkey = regrea_clef01
   'Inparams.Ssubkeyname = regrea_clef02
   'Inparams.Svaluename = regrea_clef03

if regrea_mode=0 then
r2egrea_mode=64
end if
regrea = regrea2(r2egrea_mode, r2egrea_clef01, r2egrea_clef02, r2egrea_clef03)

IF ISNULL(regrea) THEN
r2egrea_mode=32
regrea = regrea2(r2egrea_mode, r2egrea_clef01, r2egrea_clef02, r2egrea_clef03)
else
if len(regrea) = 0 then
 r2egrea_mode=32
 regrea = regrea2(r2egrea_mode, r2egrea_clef01, r2egrea_clef02, r2egrea_clef03)
end if
end if
end function

'=== lis le registre en mode regrea_mode
function regrea2(regrea_mode, regrea_clef01, regrea_clef02, regrea_clef03)

   Set objCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
   on error resume next
   objCtx.Add "__ProviderArchitecture", regrea_mode
 
   if err.number<>0 then
      toterrcop = toterrcop +1
      msgfin03 = msgfin03 & vbcrlf & "error - __ProviderArchitecture: " & vbcrlf
      if usenam=debugname then
                              msgbox("erreur __ProviderArchitecture" & vbcrlf & err.description & vbcrlf & path01 & vbcrlf & key)
      end if
   end if
   Set objLocator = CreateObject("Wbemscripting.SWbemLocator")
   Set objServices = objLocator.ConnectServer("","root\default","","",,,,objCtx)
   Set objStdRegProv = objServices.Get("StdRegProv")

   Set Inparams = objStdRegProv.Methods_("GetStringValue").Inparameters
   Inparams.Hdefkey = regrea_clef01
   Inparams.Ssubkeyname = regrea_clef02
   Inparams.Svaluename = regrea_clef03
   set Outparams = objStdRegProv.ExecMethod_("GetStringValue", Inparams,,objCtx)

   '=== show output parameters object and the registry value HKLM\SOFTWARE\
   'WScript.Echo Outparams.GetObjectText_
   'WScript.Echo "WMI Logging is set to  " & Outparams.SValue
   regrea2 = Outparams.SValue

end function