Почему эти коллекции XElement по-разному обрабатываются XElement (XName, Object [])? - PullRequest
1 голос
/ 17 июня 2020

Я не понимаю, почему они по-разному обрабатываются XElement(XName, Object[])

using namespace System.Xml.Linq

Версия 1

$Test1 = @()
(0..2).foreach{ $Test1 += [XElement]::new("test", "test") }

$Query = [XElement]::new('root',[XElement]::new("parameters", $Test1)
)

$Query.ToString()

Вывод:

<root>
  <parameters>
    <test>test</test>
    <test>test</test>
    <test>test</test>
  </parameters>
</root>

Версия 2

[Array]$Test2 = (0..2).foreach{ [XElement]::new("test", "test") }

$Query = [XElement]::new('root', [XElement]::new("parameters", $Test2)
)

$Query.ToString()

Вывод:

<root>
  <parameters>&lt;test&gt;test&lt;/test&gt;&lt;test&gt;test&lt;/test&gt;&lt;test&gt;test&lt;/test&gt;</parameters>
</root>

$Test1.gettype() и $Test2.gettype() оба читаются как массивы, и все объекты, которые они содержат, имеют тип [XElement] с теми же членами.

Они выглядят одинаково, но обрабатываются по-разному. Я понимаю, что в версии 2 без преобразования типа в [Array] он будет читаться как тип [Collection`1].

Достаточно ли этого, чтобы вызвать проблемы?

Что я удалось определить:

[XElement]::new("Obj1", $test1[0], $test1[1], $test1[2]).ToString()

ведет себя как версия 1

[XElement]::new("Obj1", ($test1[0], $test1[1], $test1[2]) ).ToString()

ведет себя как версия 2

Это похоже на преобразование массива [XElement] версии 2 в строку, ЗАТЕМ обрабатывая его как единственный XML строковый литерал, что приводит к кодированию скобок.

(<и>)


EDIT:

ЭТО РАБОТАЕТ

$Array = foreach ($i in 1..3) { [XElement]::new("test", "test") }

$Query = [XElement]::new('root', [XElement]::new("parameters", $Array)
)

$Query.ToString()

Что-то в методе .foreach() в процессе сбора [collection'1] приводит к тому, что содержимое коллекции версии 2 (независимо от того, приведено ли оно к массив), чтобы представить расширенное текстовое представление узла, а не передавать представление объекта в XElement(XName, Object[]).

Как ни странно, когда объекты XElement отправляются на консоль, она отображает представление объекта, но если вы используете .psobject:

, вы видите, что BaseObject содержит расширенное текстовое представление узлов.

$test2.psobject 
BaseObject          : {<test>test</test>, <test>test</test>, <test>test</test>}

Вы видите имена узлов для BaseObject.

$test1.psobject
BaseObject          : {test, test, test}

Вы можете полностью игнорировать $ Query и увидеть, что еще до того, как они будут добавлены в Root XMLTree, которое Powershell уже искажает [XElement] объектов в коллекции $ Test2.

Я не знаю, что .foreach() делает иначе, чем ключевое слово foreach, большинство описаний в Интернете приравнивают их к в основном идентичны по поведению. Я попробую посмотреть, что еще найду.

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Итак, я пришел к выводу, что @jdweng прав, разница только в Операторе присваивания сложения +=. Затем я увидел ваш комментарий и подумал, что вы имеете в виду, например, @jdweng, по сути, сказал, что «ну конечно версия 2 не работает, потому что это одно задание, а не массив элементов. "

.... Итак, я пошел, чтобы доказать, что вы неправы:

Версия 1

PS C:\> $Test1 = @()
PS C:\> (0..2).foreach{ $Test1 += [System.Xml.Linq.XElement]::new("test", "test") }
PS C:\> [System.Xml.Linq.XElement]::new('root',[System.Xml.Linq.XElement]::new("parameters", $Test1)).ToString()
<root>
  <parameters>
    <test>test</test>
    <test>test</test>
    <test>test</test>
  </parameters>
</root>

PS C:\> $test1.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

PS C:\> $test1.Count
3

Отлично. Мы знаем, что у нас есть массив и что в нем 3 элемента. Это ожидается, когда мы используем Оператор присваивания сложения +=. Мы создаем новый объект и добавляем его к существующему $Test1 массиву

Теперь, чтобы доказать свою неправоту. Я настолько уверен, что вместо преобразования в [Array] я даже буду использовать тот же оператор подвыражения Array , просто чтобы добавить соли в рану:

Версия 2

PS C:\> $Test2 = @()
PS C:\> $Test2 = (0..2).foreach{ [System.Xml.Linq.XElement]::new("test", "test") }
PS C:\> [System.Xml.Linq.XElement]::new('root',[System.Xml.Linq.XElement]::new("parameters", $Test2)).ToString()
<root>
 <parameters>&lt;test&gt;test&lt;/test&gt;&lt;test&gt;test&lt;/test&gt;&lt;test&gt;test&lt;/test&gt;</parameters>
</root>

А теперь, чтобы доказать свою неправоту:

PS C:\> $test2.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

PS C:\> $test2.Count
3

Ой ...............

Они выглядят точно так же то же самое. (Потеет)

Они оба относятся к типу array. У них обоих счетчик 3. Вы правы. Это очень актуальный вопрос. Они не должны быть одного и того же типа с 3 элементами и выдавать разные результаты.

Это требует дальнейшего исследования. Что именно происходит?

Версия 1

(0..2).foreach{ 
    $Test1 += [System.Xml.Linq.XElement]::new("test", "test") 
}

Если разбить его, как компилятор интерпретирует этот оператор?:

L oop 1:

$Test1 += [System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 1:

$Test1[0] = <test>test</test>

L oop 2:

$Test1 += [System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 2:

$Test1[0] = <test>test</test>
$Test1[1] = <test>test</test>

L oop 3:

$Test1 += [System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 3:

$Test1[0] = <test>test</test>
$Test1[1] = <test>test</test>
$Test1[2] = <test>test</test>

Это ваш ожидаемый результат.

Теперь давайте посмотрим на вторая версия:

Версия 2

$Test2 = @()
$Test2 = (0..2).foreach{ [System.Xml.Linq.XElement]::new("test", "test") }

L oop 1:

[System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 1:

<test>test</test>

L oop 2:

[System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 2:

<test>test</test><test>test</test>

L oop 3:

[System.Xml.Linq.XElement]::new("test", "test")

Результат L oop 3:

<test>test</test><test>test</test><test>test</test>

Final Assignment

$Test2 = <test>test</test><test>test</test><test>test</test>

Вместо добавления дополнительных элементов массива в массив вы предварительно создаете свои элементы, затем присваивая его в массив.

Мы можем увидеть это лучше, если посмотрим на сами элементы:

PS C:\> $test1 |select Name,NodeType,Value,NextNode,PreviousNode,Parent


Name         : test
NodeType     : Element
Value        : test
NextNode     : <test>test</test>
PreviousNode :
Parent       : <parameters>
                 <test>test</test>
                 <test>test</test>
                 <test>test</test>
               </parameters>

Name         : test
NodeType     : Element
Value        : test
NextNode     : <test>test</test>
PreviousNode : <test>test</test>
Parent       : <parameters>
                 <test>test</test>
                 <test>test</test>
                 <test>test</test>
               </parameters>

Name         : test
NodeType     : Element
Value        : test
NextNode     :
PreviousNode : <test>test</test>
Parent       : <parameters>
                 <test>test</test>
                 <test>test</test>
                 <test>test</test>
               </parameters>

Обратите внимание, что, добавляя к массиву, он полностью построил каждый отдельный элемент узел. Каждый «новый» элемент добавлялся индивидуально, и выстраивалось полное «правильное» дерево. а как насчет Test2?

PS C:\> $test2 |select Name,NodeType,Value,NextNode,PreviousNode,Parent


Name         : test
NodeType     : Element
Value        : test
NextNode     :
PreviousNode :
Parent       :

Name         : test
NodeType     : Element
Value        : test
NextNode     :
PreviousNode :
Parent       :

Name         : test
NodeType     : Element
Value        : test
NextNode     :
PreviousNode :
Parent       :

Обратите внимание, что он не построил дерево. Это Flat . Это 3 элемента подряд. Вот почему мы получаем счет 3 . Вот почему это все еще «массив» и «выглядит» так же, но это не так. Нет следующего узла или предыдущего узла, иерархическая структура XML не создается.

Это отсутствие структуры означает, что когда вы пытаетесь построить остальную часть документа XML, $Test2 array возвращается к интерпретации как обычная строка, а не как XML.


TL; DR:

Добавляйте элементы по одному, используя += или .Add(). Это гарантирует, что новый объект, который вы создаете, будет правильно добавлен. Вы создаете 3 новых объектов, напрямую добавляете 3 ссылки на новые объекты.

Vs. Тест 2: вы создаете 3 новых объекта, объединяете выходы объектов (не ссылки), а затем назначаете выходы 3 объектов массиву, дает вам разные результаты .

0 голосов
/ 21 июня 2020

Укажите тип данных массива. Объекты обрабатываются таким образом, что Powershell ведет себя так, как будто они не относятся к типу данных [XElement]. Указание этого в переменной присваивания как [XElement[]] гарантирует, что PS правильно передает их в качестве параметров другим функциям.

[XElement[]]$array=(0..2).foreach{[XElement]::new("Name","$_")}

Это частично из-за того, как оператор .foreach() ведет себя как блок сценария PowerShell, который выполняется по методу .invoke().

$invoke={
[XElement]::new("Name","Value"),
[XElement]::new("Name","Value"),
[XElement]::new("Name","Value")
}.Invoke()

Действует как

$Array = (0..2).foreach{[XElement]::new("Name", "Value") }

$invokereturnasis={
[XElement]::new("Name","Value"),
[XElement]::new("Name","Value"),
[XElement]::new("Name","Value")
}.InvokeReturnAsIs()

Действует как

$Array = (0..2) | ForEach-Object { [XElement]::new("Name", "Value") }

Поведение ForeEach-Object Ссылка

Другие примечания:

  1. Оператор .foreach() ведет себя как блок сценария, который выполняется с метод .invoke()

  2. Вы не можете заставить оператор .foreach() использовать метод .InvokeReturnAsIs().

  3. Объекты коллекции , при индивидуальном преобразовании с использованием ForEach-Object, поскольку объекты [XElement] работают должным образом.

    [System.Xml.Linq.XElement]::new("root", ( $Array | ForEach-Object { [XElement]$_ } ))

    Действительно

Дольше Пояснение

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...