Проблема с Newtonsoft Json Linq в Powershell vs .NET? - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть следующий код в приложении C # .NET ....

 JObject myJObject = new JObject();
 JArray myJArray = new JArray();

 myJObject.Add(
                (new JProperty("PropA", (new JObject(new JProperty("PropA1", "")))))
              );

 Console.WriteLine(myJObject.ToString());

Он работает как положено, и вывод, который я получаю,

{ "PropA": { "PropA1": "" } }

Однако, когда я делаю точно такой же код в Power shell (но переведенный) ...

using namespace Newtonsoft.Json.Linq
Add-Type -Path "C:\Temp\Newtonsoft.Json.dll" 

[JObject] $myJObject = New-Object JObject
[JArray]  $myJArray = New-Object JArray

$myJObject.Add(
                (New-Object JProperty("PropA",(New-Object JObject((New-Object JProperty("PropA1",""))))))
              ) 

write-host $myJObject.ToString()       

Взрывается, и я получаю ошибку ..

New-Object: исключение, вызывающее «.ctor» с аргументом (ами) «1»: «Невозможно добавить Newtonsoft.Json.Linq.JValue в Newtonsoft.Json.Linq.JObject.»

Интересно, если я использую тот же код, но добавляю 2 свойства, он работает ...

  $myJObject.Add(
                 (New-Object JProperty("PropA",(New-Object JObject((New-Object JProperty("PropA1","")), (New-Object JProperty("PropA2",""))))))
                 ) 

но, конечно, я понимаю ...

{ "PropA": { "PropA1": "", "PropA2": "" } }

Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

ТЛ; др

using namespace Newtonsoft.Json.Linq
Add-Type -Path "C:\Temp\Newtonsoft.Json.dll" 

[JObject] $myJObject = New-Object JObject

$myJObject.Add(
  (New-Object JProperty "PropA",
                        (New-Object JObject (
                            # Note the , (...) around this New-Object call,
                            # which wraps the resulting JProperty instance in a 
                            # single-element array.    
                            , (New-Object JProperty "PropA1", "")
                          )
                        )
  )
)

$myJObject.ToString() 

Альтернатива с использованием PSv5 + статический ::new() метод , доступный для типов вызывающих конструкторов :

[JObject] $myJObject = New-Object JObject

$myJObject.Add(
  [JProperty]::new("PropA",
    [JObject]::new(
      # No workaround needed.
      [JProperty]::new("PropA1", "")
    )
  )
)

$myJObject.ToString() 

Ответ Брайана Роджерса был на правильном пути: проблема в том, что внутренний конструктор JObject не получает экземпляр JProperty, передаваемый в качестве аргумента как например при использовании вызовов командлетов New-Object.

# *Seemingly* the equivalent of: new JObject(new JProperty("PropA1", ""))
New-Object JObject (New-Object JProperty "PropA1", "") # !! FAILS

Обратите внимание на использование подобного оболочке синтаксиса - аргументы, разделенные пробелами, без скобок вокруг списка аргументов - потому что именно этого ожидает командлет, такой как New-Object - это не вызовы методов; это команды PowerShell, проанализированные в режиме аргумента .

Включение экземпляра JProperty в вспомогательный массив устраняет проблему :

New-Object JObject (, (New-Object JProperty "PropA1", "")) # OK - wrapped in array

, - это оператор построения массива PowerShell , поэтому , <expr> создает одноэлементный массив object[], охватывающий результат <expr>. [1]

Тем не менее, проблемы с можно избежать, если использовать метод PSv5 + static ::new() для построения ; ::new() доступен для всех типов и позволяет вызывать конструкторы, используя синтаксис метода :

[JObject]::new([JProperty]::new("PropA1", "")) # OK

Что касается , почему аргумент a scalar JProperty не работает с New-Object в PowerShell :

Поскольку тип JProperty реализует интерфейс IEnumerable, PowerShell пытается перечислить отдельный экземпляр JProperty вместо передачи его как самого себя при привязке к (подразумевается) ) -ArgumentList параметр (который сам object[] -тип).
Это терпит неудачу, потому что конструктор JObject затем видит результат этого перечисления, который является JValue экземпляром, представляющим свойство JSON value , и создающим экземпляр JObject из экземпляра JValue isn не разрешено, как отражено в сообщении об ошибке - см. ответ Брайана здесь .

Обтекание такого экземпляра в массиве позволяет обойти проблему: оно предотвращает перечисление экземпляра JProperty и безопасно передает его внутрь вспомогательного экземпляра object[].

Если то, что вы передаете New-Object JObject, является массивом , начинающимся с , таким как ваш пример передачи двух JProperty экземпляров, проблему также избегают.

Также обратите внимание, что приведение к [JObject] также работает, но только с one свойство:

New-Object JObject ([JObject] (New-Object JProperty "PropA1", "")) # OK with 1 prop.

Не то чтобы Powershell имеет встроенную поддержку JSON , которая позволяет удобно конвертировать хеш-таблицы и пользовательские объекты в JSON и обратно:

# Convert a nested hashtable to JSON
PS> @{ PropA = @{ PropA1 = 42 } } | ConvertTo-Json -Compress
{"PropA":{"PropA1":42}}

# Convert JSON to a custom object [pscustomobject] with property
# .PropA whose value is another custom object, with property .PropA1
PS> '{"PropA":{"PropA1":42}}' | ConvertFrom-Json

PropA
-----
@{PropA1=42}

Таким образом, если у вас нет особых требований и / или проблем с производительностью, встроенных функций PowerShell может быть достаточно, и вы даже можете конвертировать пользовательские объекты / хеш-таблицы в JObject / JToken экземпляры через строковое представление JSON, хотя не дешево:

PS> [JToken]::Parse((@{ PropA = @{ PropA1 = 42 } } | ConvertTo-Json -Compress)).ToString()
{
  "PropA": {
    "PropA1": 42
  }
}

[1] Обратите внимание, что @(...), оператор подвыражения массива, не работает надежно в этом случае, поскольку он также включает нежелательное перечисление экземпляра JProperty перед переносом результата в массив [object[]]:

# !! FAILS too: 
# The JProperty instance is again enumerated, resulting in a single
# JValue instance, which is what @(...) then wraps in a
# single-element [object[]] array.
$var = New-Object JProperty "PropA1", ""
New-Object JObject @($var)

Любопытно, что вы можете сойти с рук @(...) , если непосредственно сделаете вызов New-Object внутри него :

# !! Happens to work, but is BEST AVOIDED.
# !! Use (, (New-Object JProperty "PropA1", "")) instead.
New-Object JObject @(New-Object JProperty "PropA1", "")

Весьма неясная причина этой счастливой случайности, на которую не следует полагаться, заключается в том, что использование команды вместо выражения напрямую приводит к обычно невидимому дополнительному [psobject] обертка, которая в этом случае предотвращает перечисление экземпляра JProperty; общую справочную информацию смотрите в этом выпуске GitHub .

0 голосов
/ 09 ноября 2018

Я подозреваю, что PowerShell по какой-то причине не разрешает правильный конструктор JObject, хотя я не знаю почему. Некоторое время я играл с вашим примером и не смог убедить PowerShell поступить правильно.

Я бы предложил переписать ваш скрипт так, чтобы вы создавали свой JObjects с пустым конструктором, а затем использовали метод Add для добавления к ним свойств. Я смог получить желаемый результат следующим образом:

$emptyVal = New-Object JValue ""
$innerObject = New-Object JObject
$innerObject.Add("PropA1", $emptyVal)
$myJObject = New-Object JObject
$myJObject.Add("PropA", $innerObject)

Write-Host $myJObject.ToString()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...