Что нового

[Массивы] Как правильно вытащить данные из XML файла

CMAUJI

Новичок
Сообщения
2
Репутация
0
Добрый день.
Есть XML файл и из него необходимо вывести, в виде списка, все значений "DisplayName".

XML файл очень большой, выкладываю урезанный вариант.

Код:
<?xml version="1.0"?>
<UserGroupList>
	<UserGroupItem GUID="{7B86B856-1DF1-43B0-B90A-641E11E80C3F}" DisplayName="ad"/>
	<UserGroupItem GUID="{C744D873-1FD0-49E5-B873-B794CFB959F5}" DisplayName="H2"/>
	<UserGroupItem GUID="{FC62FF9C-89BD-4FA9-9AAC-8E035174CEDD}" DisplayName="test"/>
</UserGroupList>
Пробовал делать через массив, но что-то не получилось, пример:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen("D:\group.xml")

Dim $aAttName[1], $aAttVal[1], $ret
For $i = 1 to $i+1
	$ret = _XMLGetAttrib("/UserGroupList/*["&$i+1&"]", $aAttName, $aAttVal)
_ArrayDisplay($aAttName,"UserGroupItem")
_ArrayDisplay($aAttVal,"UserGroupItem")

	$node = _XMLGetAttrib("/UserGroupList/*["&$i+1&"]","DisplayName",'[@DisplayName='&$i&']')
MsgBox(0,"Name Where Id="&$i,$node)
if @error then ExitLoop
Next
Exit


Подскажите, пожалуйста, где ошибся.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Тут одинаковые пункты, я не думаю что это правильная запись xml, в таком случае я бы использовал RegExp:

Код:
$vTest = FileRead(@ScriptDir & "\group.xml")
$aRet = StringRegExp($vTest, '<UserGroupItem GUID=".*" DisplayName="(.*)"/>', 3)

_ArrayDisplay($aRet)


А вообще, если бы это был правильный xml, то можно было бы получать значения так:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen(@ScriptDir & "\group.xml")

$sRoot = "UserGroupList"
$aChilds = _XMLGetChildText($sRoot)
;~ _ArrayDisplay($aChilds)

Dim $aName[1], $aValue[1]

For $i = 1 To UBound($aChilds)-1
	$aAttribs = _XMLGetAllAttrib($sRoot & "/" & $aChilds[$i], $aName, $aValue)
	_ArrayDisplay($aAttribs)
Next
 
Автор
C

CMAUJI

Новичок
Сообщения
2
Репутация
0
Спасибо, за помощь.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Kaster [?]
там нет одинаковых пунктов
Ну как нет, в файле мы имеем 3 одинаковых пути «UserGroupList/UserGroupItem».
Как ты предлагаешь из этого вытащить значение/аттрибуты?
У меня не получилось.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
CreatoR
атрибутами являются GUID и DisplayName, которые имеют разные значения для каждого поля. все по формату. а по поводу как вытаскивать, честно говоря я не знаком с библиотекой XMLDomWrapper, и не в курсе, как он вытаскивает эти значения, поэтому, к сожалению, не могу ничего подсказать :(
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Kaster [?]
атрибутами являются GUID и DisplayName, которые имеют разные значения для каждого поля. все по формату
Какому формату?

Ну представь себе, ты хочешь прочитать данные из ini-файла, а там такое содержимое:

INI:
[Section]
Key1=Some Value
Key1=Other Value
Стандартные Ini* функций с этим не справляются, потому что это неправильная запись, ключ должен быть уникальным.
Так и
тут, Item'ы одинаковые, прочитать стандартным способом (в данном случае используя спец. библиотеку) не получится.

Странно, IniReadSection читает оба ключа.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
CreatoR
ini и xml отличаются. формат можно глянуть в вики. там конечно не все, но основное есть.
Синтаксис XML
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Вот, так можно оказывается:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen(@ScriptDir & "\group.xml")

$sRoot = "UserGroupList"
$aChilds = _XMLGetChildText($sRoot)

Dim $aName[1], $aValue[1]

For $i = 1 To UBound($aChilds)-1
	$aAttribs = _XMLGetAllAttribIndex($sRoot & "/" & $aChilds[$i], $aName, $aValue, "", $i-1)
	_ArrayDisplay($aAttribs)
Next



Добавлено:
Сообщение автоматически объединено:

Kaster [?]
ini и xml отличаются
Ты серъёзно думаешь что я не знаю этого? :scratch:

формат можно глянуть в вики
Я с xml форматом работал немало, и не разу не встречал одинаковые пути, но видимо оно допускается, раз уж библиотека предусматривает такие случаи.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7 790
Репутация
2 319
Господа, дайте, пожалуйста, ссылку на _XMLDomWrapper.au3.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
CreatoR
тогда мне не понятно, зачем ты приводишь в пример ini. и мне не понятно, что ты подразумеваешь под слово "путь". в xml нет никаких путей. есть корневой элемент, есть родительские узлы и дочерние, которые имеют атрибуты и значения. приведенный в шапке файл является нормальным, с точки зрения формата, xml файлом
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Kaster [?]
тогда мне не понятно, зачем ты приводишь в пример ini
Это был не совсем правильный пример, но суть была в том что не должны быть одинаковые ключи.

мне не понятно, что ты подразумеваешь под слово "путь". в xml нет никаких путей.
Как нет, в первом сообщений содержимое xml имеет путь «UserGroupList/UserGroupItem».

есть корневой элемент, есть родительские узлы и дочерние
Это и называется путь :smile:


Добавлено:
Сообщение автоматически объединено:

madmasles [?]
дайте, пожалуйста, ссылку на _XMLDomWrapper.au3.
Вот ещё тема на оф. форуме: XML DOM wrapper (COM)
 

Yuri

AutoIT Гуру
Сообщения
737
Репутация
282
Еще вот такое решение:
Код:
$file = FileOpen("group.xml", 0) ;открыть файл XML для чтения, который находится в одной директории со скриптом
If $file = -1 Then ;если не удалось открыть, тогда 
    MsgBox(16, "Ошибка", "Не удалось открыть файл.") ;сообщить об ошибке
    Exit ;и выйти
EndIf

While 1 ;в цикле
    $line = FileReadLine($file) ;читаем файл XML по-строчно в переменную $line
    If @error = -1 Then ExitLoop ;при достижении конца файла - выходим из цикла   
	If StringInStr($line, "DisplayName=") <> 0 Then	;если в текущую прочитанную строку входит подстрока DisplayName=, тогда
		$array = StringSplit($line, '"') ; разбиваем эту текущую строку на массив подстрок, разделяя кавычками
		If UBound($array) >= 6 Then	; а если в массиве от 6 и более индексов			
			ConsoleWrite($array[4] & @CRLF) ; тогда вывести в список консоли 4-й индекс.
		EndIf
	EndIf
Wend
FileClose($file) ;закрыть ранее открытый файл XML
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
Yuriy [?]
Еще вот такое решение
С первого сообщения понятно, что требуется это делать посредством библиотеки «_XMLDomWrapper.au3».
В любом случае, если уже делать не через эту библиотеку, то надёжнее через RegExp, как я и показал выше.
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
218
Через MsXML, без _XMLDomWrapper.au3 (без лишних циклов).
Код:
#include <array.au3>

$oXml = ObjCreate('MSXML2.DomDocument.3.0')
If Not IsObj($oXml) Then
	MsgBox(16, 'Ошибка', 'MsXML 3.0 не установлен.')
	Exit 1
EndIf

$oXml.async = 0

$oXml.load('D:\group.xml')
;~ $oXml.loadXML('<?xml version="1.0"?><UserGroupList><UserGroupItem GUID="{7B86B856-1DF1-43B0-B90A-641E11E80C3F}" DisplayName="ad"/><UserGroupItem GUID="{C744D873-1FD0-49E5-B873-B794CFB959F5}" DisplayName="H2"/><UserGroupItem GUID="{FC62FF9C-89BD-4FA9-9AAC-8E035174CEDD}" DisplayName="test"/></UserGroupList>')

With $oXml.parseError
	If .errorCode <> 0 Then
		MsgBox(16, 'Ошибка', 'Код ошибки: ' & .errorCode & @CRLF & _
			'Номер строки с ошибкой: ' & .line & @CRLF & _
			'Позиция ошибки в строке: ' & .linePos & @CRLF & _
			'Позиция ошибки в файле: ' & .filePos & @CRLF & _
			'Причина ошибки: ' & .reason)
		Exit 2
	EndIf
EndWith

$oNodes = $oXml.selectNodes('/UserGroupList/UserGroupItem')
If $oNodes.length <= 0 Then
	MsgBox(16, 'Ошибка', 'Список пуст.')
	Exit 3
EndIf

;for-in
;~ For $oNode In $oNodes
;~ 	ConsoleWrite($oNode.xml & @LF)
;~ Next

;for-to
Dim $aDisplayNames[$oNodes.length]
For $i = 0 To $oNodes.length-1 Step +1
	$aDisplayNames[$i] = $oNodes.item($i).getAttribute('DisplayName')
Next

_ArrayDisplay($aDisplayNames)
 

ynbIpb

Скриптер
Сообщения
399
Репутация
109
Помогите пожалуйста прочитать XML с помошь _XMLDomWrapper
Код:
<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
  <Texts>
    <Text id="TXT_PRESS_START">
      <en value="Press START" />
      <fr value="Appuyez sur START" />
    </Text>
    <Text id="TXT_ANY_KEY">
      <en value="Press anything to begin..." />
      <fr value="Appuyez sur une touche pour continuer..." />
    </Text>
    <Text id="TXT_LOADING">
      <en value="Loading... please wait!" />
      <fr value="Chargement en cours...Patientez !" />
    </Text>
    <Text id="TXT_NOT_IN_TRIAL">
      <en value="Not available in trial!" />
      <fr value="Dispo. dans le jeu complet" />
    </Text>
    <Text id="TXT_FOUND_BTM">
      <en value="You found a new Block That Matters!" />
      <fr value="Vous avez trouvé un nouveau Block That Matters !" />
    </Text>
    <Text id="TXT_PRESS_ANYTHING">
      <en value="Press anything to continue" />
      <fr value="Appuyez sur une touche pour continuer" />
    </Text>
    <Text id="TXT_ONE_HUNDRED">
      <en value="* Yayz! Get your cheap reward on swingswingsubmarine.com/BTM100 pswd: " />
      <fr value="* Bravo ! Une surprise vous attend sur swingswingsubmarine.com/BTM100 pswd: " />
    </Text>
    <Text id="TXT_ONE_HUNDRED_CHEATED">
      <en value="* You cheated to complete the game... no reward for this :], reset your game save!" />
      <fr value="* Quoi, vous avez triché pour finir le jeu ? Pas de cadeau alors :], effacez votre sauvegarde !" />
    </Text>
и т.д.
Нужно получить: Text id=, en value=, fr value=
 

Zaramot

I ♥ AutoIt
Сообщения
1 160
Репутация
657
Так сойдёт:
Код:
#include <Array.au3>

$XML = FileOpenDialog('Choose XML file', '', 'XML File (*.xml)')
If @error Then Exit
$Read = FileRead($XML)
$RegExp = StringRegExp($Read, '<.*="(.*?)"', 3)

_ArrayDisplay($RegExp)
 

ynbIpb

Скриптер
Сообщения
399
Репутация
109
Вот это я понимаю оптимизированный код :shok:
Не то, что мой:
Код:
$sTexts_xml_Path = FileOpenDialog ( "Выберите XML файл в формате UTF-8 (BOM)", @ScriptDir, "XML (*.xml)|Все (*.*)", 1)
If $sTexts_xml_Path = "" Then Exit
$hTexts_xml = FileOpen ($sTexts_xml_Path, 0+16) ; сначала открываем файл в бинарном режиме для чтения сигнатуры
$bBOM = FileRead ($hTexts_xml, 3)
FileClose ($hTexts_xml)
If $bBOM <> "0xEFBBBF" Then ; если сигнатура не равна BOM, возвращаемся
	MsgBox (0, "", "Файл не UTF-8 (BOM)")
	Exit
EndIf
$hTexts_xml = FileOpen ($sTexts_xml_Path, 0+128) ; открываем для чтения в UTF-8
$sTexts_xml_Data = FileRead ($hTexts_xml)
FileClose ($hTexts_xml)
$aTexts_xml_Data = StringSplit (StringStripCR ($sTexts_xml_Data), @LF); разбиваем на строки
$sResult = ""
For $i=1 To $aTexts_xml_Data[0]
	$iPos = StringInStr ($aTexts_xml_Data[$i], '<Text id="')
	If $iPos > 0 Then
		$aTexts_xml_Data[$i] = StringTrimLeft ($aTexts_xml_Data[$i], $iPos + 9)
		$iQts = StringInStr ($aTexts_xml_Data[$i], '"', 0, -1); определяем позицию кавычки с конца
		$sResult &= "[" & StringMid ($aTexts_xml_Data[$i], 1, $iQts-1) & "]" & @CRLF
		$iPosUn = StringInStr ($aTexts_xml_Data[$i+1], '<univ value="')
		If $iPosUn > 0 Then
			$aTexts_xml_Data[$i+1] = StringTrimLeft ($aTexts_xml_Data[$i+1], $iPosUn + 12)
			$iQts = StringInStr ($aTexts_xml_Data[$i+1], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "univ=" & StringMid ($aTexts_xml_Data[$i+1], 1, $iQts-1) & @CRLF
		EndIf
		$iPosEn = StringInStr ($aTexts_xml_Data[$i+1], '<en value="')
		If $iPosEn > 0 Then
			$aTexts_xml_Data[$i+1] = StringTrimLeft ($aTexts_xml_Data[$i+1], $iPosEn + 10)
			$iQts = StringInStr ($aTexts_xml_Data[$i+1], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "en=" & StringMid ($aTexts_xml_Data[$i+1], 1, $iQts-1) & @CRLF
		EndIf
		$iPosFr = StringInStr ($aTexts_xml_Data[$i+2], '<fr value="')
		If $iPosFr > 0 Then
			$aTexts_xml_Data[$i+2] = StringTrimLeft ($aTexts_xml_Data[$i+2], $iPosEn + 10)
			$iQts = StringInStr ($aTexts_xml_Data[$i+2], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "fr=" & StringMid ($aTexts_xml_Data[$i+2], 1, $iQts-1) & @CRLF
		EndIf
	EndIf
Next
$hTexts_xml = FileOpen ($sTexts_xml_Path & ".txt", 2) 
FileWrite ($hTexts_xml, $sResult)
FileClose ($hTexts_xml)
MsgBox (0, "", "Done!")

Но хочется именно средствами _XMLDomWrapper, так как мне ведь ещё и обратно эти данные заносить придётся.
А так спасибо за вариант.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7 790
Репутация
2 319
ynbIpb [?]
Но хочется именно средствами _XMLDomWrapper
Я с ними плохо знаком. :(
Вариант с RegExp:
Код:
Dim $aResult[1][3] = [['id', 'en', 'fr']]
$iError = 1
$sFileXml = @ScriptDir & '\test.xml'
$sNewText = ''
$hFile = FileOpen($sFileXml, FileGetEncoding($sFileXml))
If $hFile = -1 Then
	MsgBox(16, 'Error', 'FileOpen')
	Exit
EndIf
$sRead = FileRead($hFile)
FileClose($hFile)
$aTemp = StringRegExp($sRead, '(?s)<Text(.*?)</Text>', 3)
If @error Then
	MsgBox(16, 'Error', 'StringRegExp')
	Exit
EndIf
For $i = 1 To 1
	For $i = 1 To UBound($aTemp)
		For $j = 0 To UBound($aResult, 2) - 1
			$sValue = StringRegExpReplace($aTemp[$i - 1], '(?s).*?' & $aResult[0][$j] & '.*?="(.*?)"\W?.*', '$1')
			If @extended <> 1 Then ExitLoop 3
			If $j Then
				$sNewText &= $aResult[0][$j] & '=' & $sValue & @CRLF
			Else
				$sNewText &= '[' & $sValue & ']' & @CRLF
			EndIf
		Next
	Next
	$hFile = FileOpen($sFileXml & '.ini', 2)
	If $hFile = -1 Then ExitLoop
	If Not FileWrite($hFile, StringTrimRight($sNewText, 2)) Then ExitLoop
	FileClose($hFile)
	$iError = 0
Next
If $iError Then
	MsgBox(16, 'Error', 'Error')
Else
	MsgBox(64, 'Info', 'OK')
EndIf


Не знаю, правильно или нет, но у меня работает:
Код:
#include '_XMLDomWrapper.au3'
#include <Array.au3>

$sTexts = '/XnaContent/Texts/Text'
Dim $aName[1], $aValue[1], $aResult[1][1]

$GroupXML = _XMLFileOpen(@ScriptDir & '\test.xml')
$iCount = _XMLGetNodeCount($sTexts)
$aText = _XMLGetChildText($sTexts)
ReDim $aResult[$iCount + 1][$aText[0] + 1]
For $i = 1 To $aText[0]
	$aResult[0][$i] = $aText[$i]
Next
For $i = 1 To $iCount
	_XMLGetAllAttribIndex($sTexts, $aName, $aValue, '', $i - 1)
	If Not $aResult[0][0] Then
		$aResult[0][0] = $aName[0]
	EndIf
	$aResult[$i][0] = $aValue[0]
	For $j = 1 To $aText[0]
		_XMLGetAllAttribIndex($sTexts & '/' & $aText[$j], $aName, $aValue, '', $i - 1)
		$aResult[$i][$j] = $aValue[0]
	Next
Next
_ArrayDisplay($aResult)
 
Верх