{"id":1887,"date":"2014-10-21T00:00:00","date_gmt":"2014-10-21T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/getting-data-into-and-out-of-powershell-objects\/"},"modified":"2016-09-28T15:16:23","modified_gmt":"2016-09-28T15:16:23","slug":"getting-data-into-and-out-of-powershell-objects","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/getting-data-into-and-out-of-powershell-objects\/","title":{"rendered":"Getting Data Into and Out of  PowerShell Objects"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"start\">When you need to pass the data of an object, in all its hierarchical intricacy, to another process, \u00a0or save it to storage, there was, until recently, no alternative to XML for &#8216;serialization&#8217; other than <a href=\"http:\/\/en.wikipedia.org\/wiki\/Abstract_Syntax_Notation_One\">ASN.1<\/a> (Abstract Syntax Notation One).\u00a0 However, every computer language has a way of describing and representing data structures. PowerShell has its own terse, but powerful, style \u00a0of \u00a0object notation, richer than JSON, and easier to comprehend than XML. Why, I thought, was there no <b>ConvertTo-PSON<\/b> (PowerShell Object Notation) that would give us the means to get the PowerShell script for the data from objects, in a format that is capable of recreating the object, data-wise; Just like JSON.stringify()?. Why not, when one comes to think about it, is there no <b><code>ConvertTo-YAML<\/code><\/b> to make is easier to inspect this data as if it were in a hierarchical list?<\/p>\n<p>&#8216;Why not?&#8217;, I muttered, as I strode to the keyboard.<\/p>\n<h2>Object Notation<\/h2>\n<p>Any data object, in the same way as \u00a0a database table,\u00a0 is pretty useless without the means to easily get the data into or out of it. To transfer object data across a network, or to save it in a database or file, it has to be &#8216;serialized&#8217; into a representation of its object hierarchy, generally in XML.\u00a0 When it is rehydrated, or &#8216;de-serialized&#8217;, the reverse process recreates the object hierarchy. In PowerShell, this sometimes happens under the covers when accessing remote object data.<\/p>\n<p>We are most familiar with JSON as an object notation, because it is increasingly used for data exchange and storage, but it started life as being almost 100% standard JavaScript. \u00a0It was valuable because it was so easy to &#8216;de-serialize&#8217; because one could, if one was feeling reckless, merely execute it as JavaScript code. \u00a0You can produce JSON in JavaScript with <code>JSON.stringify()<\/code> function. JavaScript isn&#8217;t the only language where one can pull this trick: Every .NET language has its own way of representing the data within objects for the purposes of construction or persistence. \u00a0PowerShell is no exception:\u00a0 Instead of the JSON array notation &#8216;[&#8216; and &#8216;]&#8217;, you have &#8216;@{&#8216; and &#8216;}&#8217;, and the &#8216;{&#8216; &#8216;}&#8217; blocks need a\u00a0 &#8216;@ in the front &#8216;@{&#8216; &#8216;}&#8217;, to make it into a hash table. The colon &#8216;:&#8217; becomes the assignment operator &#8216;=&#8217;. (&lt;Pedantry&gt; PowerShell officially doesn&#8217;t have an array notation, \u00a0the @( \u00a0&#8230; ) is an array sub-expression.&lt;\/pedantry&gt;)<\/p>\n<p>I used the term &#8216;reckless&#8217; to describe the old habits of executing Object Notations. JSON and PSON (and to a lesser extent YAML) have a fundamental security weakness. Although a JSON document could merely be executed in order to create the JavaScript object, any JavaScript would be executed. This would be an opportunity for a malicious hacker to get code executed.\u00a0 The same is true of PSON. You just execute it with invoke-expression.\u00a0 It is just too easy to slip in malicious PowerShell code. With JavaScript they closed the exploit by adding <code>JSON.parse()<\/code>, which is now in the \u00a0ECMA-262 standard.\u00a0 I know of no way of doing this with PSON<code>. <\/code>I put in a rabbit-proof fence of Regex before doing anything like this.<b><\/b><\/p>\n<p>PowerShell&#8217;s PSON\u00a0 (PowerShell Object Notation)\u00a0 is more comprehensive than JSON\u00a0 in that it allows you to specify the datatype, and allows many more datatypes. \u00a0YAML, by contrast, was designed to be as easy as possible for humans to read, but it has made it hard to create a parser for it.<\/p>\n<p>So, the classic JSON example<\/p>\n<pre>\"menu\": {\"id\": \"file\",\r\n\"value\": \"File\",\r\n\u00a0\u00a0\"popup\": {\r\n\u00a0\u00a0\u00a0\u00a0\"menuitem\": [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0]\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0}\r\n<\/pre>\n<p>\u00a0&#8230; becomes the similar-looking PowerShell equivalent &#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">@{\r\n\u00a0 'menu'= [ordered]@{\"id\"= \"file\";\r\n\u00a0 'value'= \"File\";\r\n\u00a0 'popup'= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"menuitem\"= @(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"New\"; \"onclick\"= \"CreateNewDoc()\"},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"Open\"; \"onclick\"= \"OpenDoc()\"},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"Close\"; \"onclick\"= \"CloseDoc()\"}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n<\/pre>\n<p>&#8230;as you will see if you then pass it through <code>ConvertTo-JSON -depth 4 to get...<\/code><\/p>\n<pre>{\r\n\u00a0 \"menu\": {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\"id\": \"file\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\"value\": \"File\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\"popup\": {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\"menuitem\": [\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"onclick\": \"CreateNewDoc()\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"value\": \"New\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"onclick\": \"OpenDoc()\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"value\": \"Open\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 },\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"onclick\": \"CloseDoc()\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"value\": \"Close\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0}\r\n} \r\n<\/pre>\n<p class=\"PowerShell\">In YAML, this becomes<\/p>\n<pre>---\r\nmenu:\r\n\u00a0 id: file\r\n\u00a0 value: File\r\n\u00a0 popup:\r\n\u00a0\u00a0\u00a0 menuitem:\r\n\u00a0\u00a0\u00a0 - value: New\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 onclick: CreateNewDoc()\r\n\u00a0\u00a0\u00a0 - value: Open\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 onclick: OpenDoc()\r\n\u00a0\u00a0\u00a0 - value: Close\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 onclick: CloseDoc()\r\n<\/pre>\n<p>There are other good reasons for using YAML as well. I use it for embedding information in routines and procedures, it is excellent since it can be read easily, and updated automatically. It is great for document headers for the same reason. YAML is just so close to existing conventions for writing structured information that it has many uses.\u00a0 PowerShell shouldn&#8217;t be without it. To de-serialize YAML, I use YAML.NET.<\/p>\n<h2>Taking it for a spin.<\/h2>\n<p>Lets just see what a SQL Server table looks like in YAML. We&#8217;ll just grab a table in PowerShell and examine a few rows.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t$SourceTable= 'MyTable'\r\n\t$Sourceinstance='MyInstance'\r\n\t$Sourcedatabase='MyDatabase'\r\n\ttry\r\n\t{\r\n\t\u00a0$SourceConnectionString = \"Data Source=$Sourceinstance;Initial Catalog=$Sourcedatabase;Integrated Security=True\"\r\n\t\u00a0$sql = \"select top 20 * FROM $SourceTable\"\r\n\t\u00a0$SqlConnection = new-object System.Data.SqlClient.SqlConnection\r\n\t\u00a0$SqlConnection.ConnectionString = $SourceConnectionString\r\n\t\u00a0$SqlCommand = $SqlConnection.CreateCommand()\r\n\t\u00a0$SqlCommand.CommandText = $sql \r\n\t\u00a0$DataAdapter = new-object System.Data.SqlClient.SqlDataAdapter $SqlCommand\r\n\t\u00a0$dataset = new-object System.Data.Dataset\r\n\t\u00a0$DataAdapter.Fill($dataset)\r\n\t\u00a0ConvertTo-YAML $dataset.Tables[0]\r\n\t}\r\n\tcatch\r\n\t{\r\n\t\u00a0$ex = $_.Exception\r\n\t\u00a0Write-Error \"whilst opening source $Sourceinstance . $Sourcedatabase . $SourceTable : Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) \"\r\n\t}\r\n\t<\/pre>\n<p>Here is just one row from AdventureWorks contact table.<\/p>\n<pre>- \r\n\u00a0\u00a0\u00a0 ModifiedDate: 2005-05-16T16:33:33 \r\n\u00a0\u00a0\u00a0 LastName: 'Ackerman' \r\n\u00a0\u00a0\u00a0 rowguid: 'df1fb8ab-2323-4330-9ab8-54e13ce6d8f9' \r\n\u00a0\u00a0\u00a0 MiddleName: '' \r\n\u00a0\u00a0\u00a0 EmailPromotion: 0 \r\n\u00a0\u00a0\u00a0 EmailAddress: 'pilar1@adventure-works.com' \r\n\u00a0\u00a0\u00a0 PasswordSalt: '\/RPjvXw=' \r\n\u00a0\u00a0\u00a0 Suffix: '' \r\n\u00a0\u00a0\u00a0 Title: 'Sra.' \r\n\u00a0\u00a0\u00a0 PasswordHash: 'cql0lRWe1D\/voQgg+XSLgdjSKgHBuM1DonTpX9ru0x8=' \r\n\u00a0\u00a0\u00a0 ContactID: 5 \r\n\u00a0\u00a0\u00a0 FirstName: 'Pilar' \r\n\u00a0\u00a0\u00a0 AdditionalContactInfo: &gt;\r\n\u00a0\u00a0\u00a0\u00a0 &lt;AdditionalContactInfo xmlns=\"http:\/\/schemas.microsoft.com\/sqlserver\/2004\/07\/adv\r\n\u00a0\u00a0\u00a0\u00a0 enture-works\/ContactInfo\" xmlns:crm=\"http:\/\/schemas.microsoft.com\/sqlserver\/2004\r\n\u00a0\u00a0\u00a0\u00a0 \/07\/adventure-works\/ContactRecord\" xmlns:act=\"http:\/\/schemas.microsoft.com\/sqlse\r\n\u00a0\u00a0\u00a0 \u00a0rver\/2004\/07\/adventure-works\/ContactTypes\"&gt;&lt;crm:ContactRecord date=\"2002-01-01Z\"\r\n\u00a0\u00a0\u00a0\u00a0 &gt;Sales contacted this customer for the first time at&lt;act:telephoneNumber&gt;&lt;act:nu\r\n\u00a0\u00a0\u00a0\u00a0 mber&gt;432-4444&lt;\/act:number&gt;&lt;\/act:telephoneNumber&gt;We talked about the Road bike price\r\n\u00a0\u00a0\u00a0\u00a0 drop and the new spring models. Customer provided us new mobile number&lt;act:mob\r\n\u00a0\u00a0\u00a0\u00a0 ile&gt;&lt;act:number&gt;432-555-7809&lt;\/act:number&gt;&lt;\/act:mobile&gt;&lt;\/crm:ContactRecord&gt;&lt;\/Addi\r\n\u00a0\u00a0\u00a0\u00a0 tionalContactInfo&gt;\r\n\u00a0\r\n\u00a0\u00a0\u00a0 NameStyle: 'False' \r\n\u00a0\u00a0\u00a0 Phone: '1 (11) 500 555-0132'\r\n<\/pre>\n<p>&#8230;and here are the first five rows from the production.Location table in PowerShell (PSON) instead.<\/p>\n<pre>@(\r\n\u00a0\u00a0\u00a0 @{ 'CostRate' = 0.0000 ; 'ModifiedDate' = 1998-06-01T00:00:00 ; 'Name' = 'Tool Crib' ; 'Availability' = 0.00 ; 'LocationID' = 1\r\n\u00a0\u00a0\u00a0 } ,\r\n\u00a0\u00a0\u00a0 @{ 'CostRate' = 0.0000 ; 'ModifiedDate' = 1998-06-01T00:00:00 ; 'Name' = 'Sheet Metal Racks' ; 'Availability' = 0.00 ; 'LocationID' = 2\r\n\u00a0\u00a0\u00a0 } ,\r\n\u00a0\u00a0\u00a0 @{ 'CostRate' = 0.0000 ; 'ModifiedDate' = 1998-06-01T00:00:00 ; 'Name' = 'Paint Shop' ; 'Availability' = 0.00 ; 'LocationID' = 3\r\n\u00a0\u00a0\u00a0 } ,\r\n\u00a0\u00a0\u00a0 @{ 'CostRate' = 0.0000 ; 'ModifiedDate' = 1998-06-01T00:00:00 ; 'Name' = 'Paint Storage' ; 'Availability' = 0.00 ; 'LocationID' = 4\r\n\u00a0\u00a0\u00a0 } ,\r\n\u00a0\u00a0\u00a0 @{ 'CostRate' = 0.0000 ; 'ModifiedDate' = 1998-06-01T00:00:00 ; 'Name' = 'Metal Storage' ; 'Availability' = 0.00 ; 'LocationID' = 5\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0 )\r\n<\/pre>\n<p>Here is a bit of RSS feed rendered the same way. (the PowerShell script is in the\u00a0 header of the article).<\/p>\n<pre>\u00a0Feed: 'SQLServerCentral.com Articles tagged Editorial' \r\n\u00a0Title: 'Challenge Yourself' \r\n\u00a0Description: &gt;\r\n\u00a0\u00a0 How do you grow your career? Steve Jones has a few ideas on what you can do to both\r\n\u00a0\u00a0 improve your skills and build your brand.\r\n\u00a0\r\n\u00a0Publication: 'SQL Server Central' \r\n\u00a0Stream: 'Editorials' \r\n\u00a0PageURL: 'http:\/\/www.sqlservercentral.com\/Articles\/Editorial' \r\n\u00a0PubDate: 2014-09-30T06:00:00 \r\n\u00a0author:\u00a0 author=null \r\n\u00a0Ago: '16 days ago' \r\n\u00a0link: 'http:\/\/www.sqlservercentral.com\/articles\/Editorial\/116527\/'\r\n---\r\n\r\n\u00a0Feed: 'SQLServerCentral.com Articles tagged Editorial' \r\n\u00a0Title: 'Database Checkups' \r\n\u00a0Description: &gt;\r\n\u00a0\u00a0 It's important that you are watching your databases' health to be sure that you can\r\n\u00a0\u00a0 make changes, as well as rollback patches when issues occur.\r\n\u00a0\r\n\u00a0Publication: 'SQL Server Central' \r\n\u00a0Stream: 'Editorials' \r\n\u00a0PageURL: 'http:\/\/www.sqlservercentral.com\/Articles\/Editorial' \r\n\u00a0PubDate: 2014-09-29T06:00:00 \r\n\u00a0author:\u00a0 author=null \r\n\u00a0Ago: '17 days ago' \r\n\u00a0link: 'http:\/\/www.sqlservercentral.com\/articles\/Editorial\/116526\/'\r\n\u00a0\r\n<\/pre>\n<h2>Creating a ConvertTo Cmdlet.<\/h2>\n<p>There was once only one available object-notation conversion in PowerShell and that was to XML using <code>ConvertTo-XML<\/code>. You can access XML directly using dot-notation. To create JSON output from a PowerShell object, you can now use the built-in <code>ConvertTo-JSON<\/code>, and there is a symmetrical <code>ConvertFrom-JSON.<\/code> \u00a0There is no equivalent <code>ConvertTo-PSON<\/code> or <code>ConvertTo-YAML<\/code>, but It is comparatively easy to create them. Theoretically, you don&#8217;t need a <code>ConvertFrom-PSON<\/code> because you can just execute the string as a scriptblock though in reality it would be wise to have one to prevent a security loophole. You can convert from YAML with YAML.NET.<\/p>\n<p>Here is a quick demo that shows a PowerShell object being converted into a string, which is then executed and finally turned into JSON to check that nothing got lost!<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">@{\"menu\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"popup\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"menuitem\"= @( #Array of hashes\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"New\"; \"onclick\"= 'CreateNewDoc()'},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"Open\"; \"onclick\"= 'OpenDoc()'},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{\"value\"= \"Close\"; \"onclick\"= 'CloseDoc()'}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0 }\r\n} | ConvertTo-PSON |invoke-expression |ConvertTo-JSON -Depth 5\r\n<\/pre>\n<p>Since the built-in <code>ConvertTo-XML<\/code> and <code>ConvertTo-JSON<\/code> both do a lot of\u00a0 work to produce their object notation objects, it would seem, at first glance, wisest to just use these, with some clever Regex Strings to subsequently \u00a0interpret into PowerShell object notation. \u00a0However, although you can get quick results this way, \u00a0it starts to get complicated when you try to refine it: You are limited in what you can do with PowerShell data types such as script blocks and XML. Likewise, you can implement a <code>ConvertTo-YAML<\/code> very quickly using <code>YamlNet<\/code> or<code> SharpYAML<\/code> but YAML can be represented in a number of ways (it is effectively a superset of <code>JSON<\/code> so you could use <code>ConvertTo-JSON<\/code>!) and it is likely you&#8217;ll want more control. (I use Scott Muc&#8217;s<code> PowerYaml<\/code>)<\/p>\n<p>In creating these Cmdlets, I chose, instead, to use a recursive routine that could keep a count of the recursion level for formatting purposes, and which\u00a0 iterated through the arrays and hash tables using <code>ForEach<\/code>.\u00a0\u00a0 This gives a lot of freedom in choosing how the various types of data are displayed. \u00a0You can use a strict &#8216;canonical&#8217; form that specifies the datatype to avoid ambiguity, or use a looser, more readable form. You can indent the code as you wish. Just alter the function to taste.<\/p>\n<p>The only real difficulty I hit was in dealing with the typical PowerShell objects such as <code>Process \u00adThread \u00adCollections<\/code>,\u00a0 or SMO database classes.\u00a0 It is easy got get this information, held as properties, but there are huge numbers of them.\u00a0 There is little consistency in the way the various Cmdlets deal with this.<\/p>\n<p>Try this just to get a flavour of the enormous amount of\u00a0 data in just the first contained objects of a <code>System.Diagnostics.Process<\/code> object , try this..<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$xml=Get-process wi* | ConvertTo-XML\r\n$xml.OuterXml\r\n<\/pre>\n<p>Even if you could, It is most unlikely that you&#8217;d need them all since many are obsolete. \u00a0I ended up reckoning that,\u00a0 for\u00a0 <code>ConvertTo-PSON<\/code> and <code>ConvertTo-YAML<\/code> that it is better to \u00a0specify the parameters you&#8217;re interested in, using the <code>S<\/code><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Process wi* |\u00a0 Select-Object Handles,NPM,PM,WS,VM,CPU,Id,ProcessName | ConvertTo-PSON <\/pre>\n<p>Just to show you that I&#8217;m no wimp, I settled on an algorithm that prevented any further recursion on a property of a complex object, and merely told you what the value was if it is a simple leaf value, or else the type of object it is. This is what <code>ConvertTo-XML<\/code> does. This still makes the lights dim when it hits something like an SMO Database or Server object. It is much better to tell it what you want via <code>Select-Object.<\/code><\/p>\n<p>. You can render the object represented by the XML or you can do it just as an InnerXML string. The former method allows us to convert XML files directly into YAML or PSON.\u00a0 Useful? Sure. It is easier to demo than explain. First\u00a0 rendering it as an object&#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t \t$xml= [Xml] @'\r\n\t\u00a0&lt;emp id=\"salary=\"'60000'&gt;\r\n\t\u00a0&lt;name&gt;\r\n\t\u00a0 &lt;first&gt;William&lt;\/first&gt;\r\n\t\u00a0 &lt;last&gt;Murphy&lt;\/last&gt;\r\n\t\u00a0&lt;\/name&gt;\r\n\t\u00a0&lt;spouse&gt;\r\n\t\u00a0 &lt;name&gt;\r\n\t\u00a0\u00a0 &lt;first&gt;Cecilia Bertha matilda&lt;\/first&gt;\r\n\t\u00a0\u00a0 &lt;last&gt;Murphy&lt;\/last&gt;\r\n\t\u00a0 &lt;\/name&gt;\r\n\t\u00a0&lt;\/spouse&gt;\r\n\t\u00a0&lt;dept id=\"K55\"\"'&gt;Finance&lt;\/dept&gt;\r\n\t&lt;\/emp&gt;\r\n\t'@\r\n\t\u00a0ConvertTo-YAML $xml\r\n\t<\/pre>\n<p>&#8230;giving &#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t---\r\n\t\r\n\t\u00a0emp:\u00a0\u00a0 \r\n\t\u00a0\u00a0 dept:\u00a0\u00a0 \r\n\t\u00a0\u00a0\u00a0\u00a0 #text:\u00a0\u00a0 'Finance' \r\n\t\u00a0\u00a0\u00a0\u00a0 id:\u00a0\u00a0 'K55' \r\n\t\u00a0\u00a0 id:\u00a0\u00a0 '12345' \r\n\t\u00a0\u00a0 name:\u00a0\u00a0 \r\n\t\u00a0\u00a0\u00a0\u00a0 first:\u00a0\u00a0 'William' \r\n\t\u00a0\u00a0\u00a0\u00a0 last:\u00a0\u00a0 'Murphy' \r\n\t\u00a0\u00a0 salary:\u00a0\u00a0 '60000' \r\n\t\u00a0\u00a0 spouse:\u00a0\u00a0 \r\n\t\u00a0\u00a0\u00a0\u00a0 name:\u00a0\u00a0 \r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 first:\u00a0\u00a0 'Cecilia Bertha matilda' \r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 last:\u00a0\u00a0 'Murphy' \r\n\t\r\n\t<\/pre>\n<p>and the other way, simply giving it as an XML fragment, (note the parameter if you want that behaviour)\u00a0 is.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t\t$xml= [Xml] @'\r\n\t\u00a0&lt;emp id=\"salary=\"'60000'&gt;\r\n\t\u00a0&lt;name&gt;\r\n\t\u00a0 &lt;first&gt;William&lt;\/first&gt;\r\n\t\u00a0 &lt;last&gt;Murphy&lt;\/last&gt;\r\n\t\u00a0&lt;\/name&gt;\r\n\t\u00a0&lt;spouse&gt;\r\n\t\u00a0 &lt;name&gt;\r\n\t\u00a0\u00a0 &lt;first&gt;Cecilia Bertha matilda&lt;\/first&gt;\r\n\t\u00a0\u00a0 &lt;last&gt;Murphy&lt;\/last&gt;\r\n\t\u00a0 &lt;\/name&gt;\r\n\t\u00a0&lt;\/spouse&gt;\r\n\t\u00a0&lt;dept id=\"K55\"\"'&gt;Finance&lt;\/dept&gt;\r\n\t&lt;\/emp&gt;\r\n\t'@\r\n\t\u00a0ConvertTo-YAML $xml -XMLAsInnerXML 1\r\n\t<\/pre>\n<p>&#8230; giving &#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t---\r\n\t|\r\n\t&lt;emp id=\"salary=\"\"60000\"&gt;&lt;name&gt;&lt;first&gt;William&lt;\/first&gt;&lt;last&gt;Murphy&lt;\/last&gt;&lt;\/name&gt;&lt;spouse&gt;&lt;name&gt;&lt;first&gt;Cecilia Bertha matilda&lt;\/first&gt;&lt;last&gt;Murphy&lt;\/last&gt;&lt;\/name&gt;&lt;\/spouse&gt;&lt;dept id=\"K55\"\"&gt;Finan\r\n\tce&lt;\/dept&gt;&lt;\/emp&gt; \r\n<\/pre>\n<p>So, here is the basic function to produce YAML from a PowerShell object, ranging from an integer to a complex object.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function ConvertTo-YAML\r\n{\r\n<#\r\n .SYNOPSIS\r\n   creates a YAML description of the data in the object\r\n .DESCRIPTION\r\n   This produces YAML from any object you pass to it. It isn't suitable for the huge objects produced by some of the cmdlets such as Get-Process, but fine for simple objects\r\n .EXAMPLE\r\n   $array=@()\r\n   $array+=Get-Process wi* |  Select-Object Handles,NPM,PM,WS,VM,CPU,Id,ProcessName \r\n   ConvertTo-YAML $array\r\n\r\n .PARAMETER Object \r\n   the object that you want scripted out\r\n .PARAMETER Depth\r\n   The depth that you want your object scripted to\r\n .PARAMETER Nesting Level\r\n   internal use only. required for formatting\r\n#>\r\n    \r\n    [CmdletBinding()]\r\n    param (\r\n        [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]\r\n        [AllowNull()]\r\n        $inputObject,\r\n        [parameter(Position = 1, Mandatory = $false, ValueFromPipeline = $false)]\r\n        [int]$depth = 16,\r\n        [parameter(Position = 2, Mandatory = $false, ValueFromPipeline = $false)]\r\n        [int]$NestingLevel = 0,\r\n        [parameter(Position = 3, Mandatory = $false, ValueFromPipeline = $false)]\r\n        [int]$XMLAsInnerXML = 0\r\n    )\r\n    \r\n    BEGIN { }\r\n    PROCESS\r\n    {\r\n        If ($inputObject -eq $null -and !($inputObject -ne $null)) { $p += 'null'; return $p } # if it is null return null\r\n        if ($NestingLevel -eq 0) { '---' }\r\n        \r\n        $padding = [string]'  ' * $NestingLevel # lets just create our left-padding for the block\r\n        try\r\n        {\r\n            $Type = $inputObject.GetType().Name # we start by getting the object's type\r\n            if ($Type -ieq 'Object[]') { $Type = \"$($inputObject.GetType().BaseType.Name)\" } #what it really is\r\n            if ($depth -ilt $NestingLevel) { $Type = 'OutOfDepth' } #report the leaves in terms of object type\r\n            elseif ($Type -ieq 'XmlDocument' -or $Type -ieq 'XmlElement')\r\n            {\r\n                if ($XMLAsInnerXML -ne 0) { $Type = 'InnerXML' }\r\n                else\r\n                { $Type = 'XML' }\r\n            } # convert to PS Alias\r\n            # prevent these values being identified as an object\r\n            if (@('boolean', 'byte', 'byte[]', 'char', 'datetime', 'decimal', 'double', 'float', 'single', 'guid', 'int', 'int32',\r\n                    'int16', 'long', 'int64', 'OutOfDepth', 'RuntimeType', 'PSNoteProperty', 'regex', 'sbyte', 'string',\r\n                    'timespan', 'uint16', 'uint32', 'uint64', 'uri', 'version', 'void', 'xml', 'datatable', 'Dictionary`2',\r\n                    'SqlDataReader', 'datarow', 'ScriptBlock', 'type') -notcontains $type)\r\n            {\r\n                if ($Type -ieq 'OrderedDictionary') { $Type = 'HashTable' }\r\n                elseif ($Type -ieq 'PSCustomObject') { $Type = 'PSObject' } #\r\n                elseif ($Type -ieq 'List`1') { $Type = 'Array' }\r\n                elseif ($inputObject -is \"Array\") { $Type = 'Array' } # whatever it thinks it is called\r\n                elseif ($inputObject -is \"HashTable\") { $Type = 'HashTable' } # for our purposes it is a hashtable\r\n                elseif (($inputObject | gm -membertype Properties |\r\n                        Select name | Where name -like 'Keys') -ne $null) { $Type = 'generic' } #use dot notation\r\n                elseif (($inputObject | gm -membertype Properties | Select name).count -gt 1) { $Type = 'Object' }\r\n            }\r\n            write-verbose \"$($padding)Type:='$Type', Object type:=$($inputObject.GetType().Name), BaseName:=$($inputObject.GetType().BaseType.Name) \"\r\n            \r\n            switch ($Type)\r\n            {\r\n                'ScriptBlock'{ \"{$($inputObject.ToString())}\" }\r\n                'InnerXML'        { \"|`r`n\" + ($inputObject.OuterXMl.Split(\"`r`n\") | foreach{ \"$padding$_`r`n\" }) }\r\n                'DateTime'   { $inputObject.ToString('s') } # s=SortableDateTimePattern (based on ISO 8601) using local time\r\n                'Byte[]'     {\r\n                    $string = [System.Convert]::ToBase64String($inputObject)\r\n                    if ($string.Length -gt 100)\r\n                    {\r\n                        # right, we have to format it to YAML spec.\r\n                        '!!binary \"\\' + \"`r`n\" # signal that we are going to use the readable Base64 string format\r\n                        $bits = @(); $length = $string.Length; $IndexIntoString = 0; $wrap = 100\r\n                        while ($length -gt $IndexIntoString + $Wrap)\r\n                        {\r\n                            $padding + $string.Substring($IndexIntoString, $wrap).Trim() + \"`r`n\"\r\n                            $IndexIntoString += $wrap\r\n                        }\r\n                        if ($IndexIntoString -lt $length) { $padding + $string.Substring($IndexIntoString).Trim() + \"`r`n\" }\r\n                        else { \"`r`n\" }\r\n                    }\r\n                    \r\n                    else { '!!binary \"' + $($string -replace '''', '''''') + '\"' }\r\n                    \r\n                }\r\n                'Boolean' {\r\n                    \"$(&{\r\n                            if ($inputObject -eq $true) { 'true' }\r\n                            Else { 'false' }\r\n                        })\"\r\n                }\r\n                'string' {\r\n                    $String = \"$inputObject\"\r\n                    if ($string -match '[\\r\\n]' -or $string.Length -gt 80)\r\n                    {\r\n                        # right, we have to format it to YAML spec.\r\n                        $folded = \">`r`n\" # signal that we are going to use the readable 'newlines-folded' format\r\n                        $string.Split(\"`n\") | foreach {\r\n                            $length = $_.Length; $IndexIntoString = 0; $wrap = 80\r\n                            while ($length -gt $IndexIntoString + $Wrap)\r\n                            {\r\n                                $breakpoint = $wrap\r\n                                $earliest = $_.Substring($IndexIntoString, $wrap).LastIndexOf(' ')\r\n                                $latest = $_.Substring($IndexIntoString + $wrap).IndexOf(' ')\r\n                                if (($earliest -eq -1) -or ($latest -eq -1)) { $breakpoint = $wrap }\r\n                                elseif ($wrap - $earliest -lt ($latest)) { $BreakPoint = $earliest }\r\n                                else { $BreakPoint = $wrap + $latest }\r\n                                if (($wrap - $earliest) + $latest -gt 30) { $BreakPoint = $wrap } # in case it is a string without spaces\r\n                                $folded += $padding + $_.Substring($IndexIntoString, $BreakPoint).Trim() + \"`r`n\"\r\n                                $IndexIntoString += $BreakPoint\r\n                            }\r\n                            if ($IndexIntoString -lt $length) { $folded += $padding + $_.Substring($IndexIntoString).Trim() + \"`r`n`r`n\" }\r\n                            else { $folded += \"`r`n`r`n\" }\r\n                        }\r\n                        $folded\r\n                    }\r\n                    else { \"'$($string -replace '''', '''''')'\" }\r\n                }\r\n                'Char'     { \"([int]$inputObject)\" }\r\n                {\r\n                    @('byte', 'decimal', 'double', 'float', 'single', 'int', 'int32', 'int16', `\r\n                        'long', 'int64', 'sbyte', 'uint16', 'uint32', 'uint64') -contains $_\r\n                }\r\n                { \"$inputObject\" } # rendered as is without single quotes\r\n                'PSNoteProperty' { \"$(ConvertTo-YAML -inputObject $inputObject.Value -depth $depth -NestingLevel ($NestingLevel + 1))\" }\r\n                'Array'    { \"$($inputObject | ForEach { \"`r`n$padding- $(ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1))\" })\" }\r\n                'HashTable'{\r\n                    (\"$($inputObject.GetEnumerator() | ForEach {\r\n                                \"`r`n$padding  $($_.Name): \" +\r\n                                (ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + 1))\r\n                            })\")\r\n                }\r\n                'Dictionary`2'{\r\n                    (\"$($inputObject.GetEnumerator() | ForEach {\r\n                                \"`r`n$padding  $($_.Key): \" +\r\n                                (ConvertTo-YAML -inputObject $_.Value -depth $depth -NestingLevel ($NestingLevel + 1))\r\n                            })\")\r\n                }\r\n                'PSObject' { (\"$($inputObject.PSObject.Properties | ForEach { \"`r`n$padding $($_.Name): \" + (ConvertTo-YAML -inputObject $_ -depth $depth -NestingLevel ($NestingLevel + 1)) })\") }\r\n                'generic'  { \"$($inputObject.Keys | ForEach { \"`r`n$padding  $($_):  $(ConvertTo-YAML -inputObject $inputObject.$_ -depth $depth -NestingLevel ($NestingLevel + 1))\" })\" }\r\n                'Object'   { (\"$($inputObject | Get-Member -membertype properties | Select-Object name | ForEach { \"`r`n$padding $($_.name):   $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $NestingLevel -NestingLevel ($NestingLevel + 1))\" })\") }\r\n                'XML'   { (\"$($inputObject | Get-Member -membertype properties | where-object { @('xml', 'schema') -notcontains $_.name } | Select-Object name | ForEach { \"`r`n$padding $($_.name):   $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))\" })\") }\r\n                'DataRow'   { (\"$($inputObject | Get-Member -membertype properties | Select-Object name | ForEach { \"`r`n$padding $($_.name):  $(ConvertTo-YAML -inputObject $inputObject.$($_.name) -depth $depth -NestingLevel ($NestingLevel + 1))\" })\") }\r\n                #  'SqlDataReader'{$all = $inputObject.FieldCount; while ($inputObject.Read()) {for ($i = 0; $i -lt $all; $i++) {\"`r`n$padding $($Reader.GetName($i)): $(ConvertTo-YAML -inputObject $($Reader.GetValue($i)) -depth $depth -NestingLevel ($NestingLevel+1))\"}}\r\n                default { \"'$inputObject'\" }\r\n            }\r\n        }\r\n        catch\r\n        {\r\n            write-error \"Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($inputObject)' Class: $($inputObject.GetType().Name) BaseClass: $($inputObject.GetType().BaseType.Name) \"\r\n        }\r\n        finally { }\r\n    }\r\n    \r\n    END { }\r\n}\r\n\t<\/pre>\n<h2>How it works.<\/h2>\n<p><code>ConvertTo-YAML<\/code> and <code>ConvertTo-PSON<\/code> are almost identical. There are just to many differences to make it worth trying to fold it all into one function but it should\u00a0 give you a start on how to do a <code>ConvertTo-CSharp<\/code> or <code>ConvertTo-VB. <\/code><\/p>\n<p>This is a recursive function that just returns the representation of whatever object is passed to it. If it is a simple data value, then it is rendered according to the rules for the notation. The only difficulties come with strings, XML, bitmaps and so on.\u00a0 If it is a complex object, then there are a number of ways it can be shredded to get at the individual elements. Where the object is a hashtable of array, one merely needs to\u00a0 pass it through a pipeline that explores and returns the branch. There could be all sorts of nested arrays and hashtables in there. Different types of objects require different treatments,\u00a0 but the various alternative pipelines are somewhat similar.<\/p>\n<p>The function is done as an advanced PowerShell function so it can be used in a pipeline\u00a0 as well. Where possible, I&#8217;ve made it work in a similar way to <code>ConvertTo-JSON<\/code>. The only difference is in the handling of complex objects, where <code>ConvertTo-JSON<\/code> and <code>ConvertTo-XML<\/code> relies on the -depth parameter to stop you from returning huge elaborate structures. I preferred just to handle them by stopping at the first level at which it found properties in complex objects. (Depth=1). I&#8217;m uneasy about NET properties and the way they&#8217;re abused. Properties generally supposed to expose the internal state of an object, which is what we want. Functions, which we avoid, return a query, or modify the state. Unfortunately, in.NET, there are plenty of functions disguised as properties.<\/p>\n<h2>Different flavors of Object Notations.<\/h2>\n<p>Because of the different requirements of any object notation, it is difficult to package up a universal solution without a bewildering number of\u00a0 switch-parameters to specify what you want. It could be better to roll your own function based on a working &#8216;core&#8217;. One could, of course, go entirely the other way and produce a function that will serialize to any object notation and positively bristles with options. I&#8217;d rather not.<\/p>\n<h3>Formatting<\/h3>\n<div class=\"indented\">\n<p>There\u00a0 are many different ways of formatting JSON. The same is true\u00a0 of PSON, because the indenting\u00a0 is pure decoration. You can leave out whitespace entirely outside string literals to save space, or have formatting optimized to make it easy on the eye.\u00a0 Few will agree on the best way of doing this. YAML is different because indenting is used, Pythonesque,\u00a0 instead of bracketing to indicate nesting. YAML has its own creative outlets since it allows JSON as a subset of YAML. Lists can be bracketed and comma-delimited.\u00a0 Long strings can be represented in a number of different ways depending on how easily you&#8217;d like the YAML to be read.\u00a0 In fact, YAML allows\u00a0 so many different choices that the creation of a YSON deserialiser is extraordinarily difficult. Yes, there are probably\u00a0 too many options and preferences to make a simple built-in serialiser entirely useful.<\/p>\n<\/div>\n<h3>Datatype definition<\/h3>\n<div class=\"indented\">\n<p>As well as formatting, both PowerShell and YAML allow a great deal of latitude in the way that a datatype is specified. In both cases, it can be left implied. It is reasonably easy to tell the difference between an integer and decimal number, and one can represent a float in a way that makes it obvious.\u00a0 Other datatypes are less obvious and can be specified.\u00a0 YAML has a canonical form that is more exact but more readable. PSON can, likewise, be liberally sprinkled with Type accelerators in square-brackets to specify the datatype unambiguously.<\/p>\n<\/div>\n<h3>Object-Depth and hierarchical-depth<\/h3>\n<div class=\"indented\">\n<p>Even though .NET objects can contain simple hierarchies consisting of simple values, arrays and hashtables, they can also have \u00a0contained objects, \u00a0and so act more like graphs\/networks.\u00a0 If you explore them in too much depth you can end up in all sorts of difficulties, particularly with loops. \u00a0Simple properties can be disguised as functions. The built-in Cmdlets \u00a0<code>ConvertTo-XML<\/code> limits depth \u00a0of contained objects arbitrarily to a set figure which prefents infinite looping. \u00a0The default value is 1; Anything more than that produces a cacophony of useless information. Worse, one can get into an endless circular reference.\u00a0 \u00a0<code>ConvertTo-XML<\/code> has a proper understanding of what a contained object (Data structures like hashtables and arrays aren&#8217;t counted as contained objects)<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$ratherNested=@{\"FirstLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"SecondLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"ThirdLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"FourthLevel\"= @('one','two','three')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0 }\r\n} | ConvertTo-XML -depth 1\r\n$ratherNested.OuterXml \r\n<\/pre>\n<p>whic<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;Objects&gt;\r\n\u00a0\u00a0  \u00a0\u00a0&lt;Object Type=\"System.Collections.Hashtable\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Key\" Type=\"System.String\"&gt;FirstLevel&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Value\" Type=\"System.Collections.Hashtable\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Key\" Type=\"System. String\"&gt;SecondLevel&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Value\" Type=\"System.Collections.Hashtable\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Key\" Type=\"System.String\"&gt;ThirdLevel&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Value\" Type=\"System.Collections.Hashtable\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Key \" Type=\"System.String\"&gt;FourthLevel&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Name=\"Value\" Type=\"System.Object[]\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Type=\"System.String\"&gt;one&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Type=\"System.String\"&gt;two&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;Property Type=\"System.String\"&gt;three&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/Property&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;\/Property&gt;\r\n\u00a0\u00a0&lt;\/Object&gt;\r\n&lt;\/Objects&gt; \r\n<\/pre>\n<p>Whereas <code>ConvertTo-JSON<\/code> has an entirely different concept of what a contained object is. It seems to have\u00a0 equated hierarchical nesting level\u00a0 with depth and produces an entirely different result.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">@{\"FirstLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"SecondLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"ThirdLevel\"= @{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"FourthLevel\"= @('one','two','three')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0 }\r\n} | ConvertTo-JSON -depth 1 \r\n<\/pre>\n<p>&#8230; producing &#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">{ \r\n\u00a0\u00a0\u00a0\"FirstLevel\": { \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"SecondLevel\": \"System.Collections.Hashtable\" \r\n\u00a0\u00a0\u00a0} \r\n}\r\n<\/pre>\n<p>In the <code>ConvertTo-YAML<\/code> and <code>-PSON<\/code> routines, \u00a0I only iterate through the parameters values of \u00a0complex objects as a last resort and then only as a leaf. I&#8217;ll describe the nature of the leaf but not shred it. This is equivalent to <code>ConvertTo-XML<\/code> at level 1. This way I can make a reasonable representation \u00a0of one of those objects without going too far\u00a0 into the details. There was a time I removed that constraint and kicked up the depth figure just to see what happened. I got the scrolling screen of terror, It looked for all the world as if half the Wikipedia and several binary images\u00a0 went scrolling up the console.<\/p>\n<p>\u00a0There is another reason for caution: Some SMO objects use lazy loading, and to iterate through all the possible data &#8216;within&#8217; contained objects would bring the SQL Server instance to its knees. They were designed to be eaten in small bites.<\/p>\n<p>More irritating for me than the byzantine complexity of many windows objects was seeing that the CMDlets such as Format-table are able to display just the more important property values of a contained object. Perhaps a kind reader can tell me how they know.\u00a0 At that point I felt there was enough complexity for an article.<\/p>\n<\/div>\n<p>The point of recounting all this is to illustrate the suggestion that there may be some virtue in &#8216;rolling your own&#8217; object serialiser for special purposes.<\/p>\n<h3>Conclusions.<\/h3>\n<p>I hope I&#8217;ve shown that there is nothing hideously difficult in &#8216;stringifying&#8217; object data into forms that can be consumed by other processes, or for the purposes of searching or exploring data.<\/p>\n<p>I&#8217;ve had mixed motives for doing this. I wanted to show how to use PowerShell to read and write YAML in headers , and to use PSON to gain object persistence in PowerShell. I wanted to suggest a more economical way of passing PowerShell Objects across a network. I also wanted to hint that instead of using XML or JSON as a universal &#8216;esperanto&#8217; data transfer format, one could use whatever format is native to the destination.<\/p>\n<p>To take a human parallel, it is easier with data transfer\u00a0 to speak a foreign language than it is to understand one. To make this point, I&#8217;ve done two PowerShell functions to convert to PowerShell Object notation and YAML. To do it in Visual Basic or C# is easy. What about SQL? A ridiculous idea, you might think, but one can use the SELECT&#8230;VALUES statement to pass data, and one could represent a hierarchy in a hierarchy table.<\/p>\n<p>I like the idea of databases being able to consume object data without the application developer having to feel responsible for the mental gymnastics required to convert the object viewpoint of data into the relational form. We currently have the technology to hand XML, in all its trickiness, through to the database to be shredded there into its relational data. Rather than repeat the process with other object notation formats, why not devise an intermediate form, a hierarchy table, that can be passed via its SQL object notation or a table-valued parameter to a stored procedure that performs the mystery of\u00a0 updating base tables with the data in a transactional way, as we once dreamed of with the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms172697.aspx\">updategram<\/a>.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>You can execute PowerShell code that creates the data of an object, but there is no cmdlet to generate the &#8216;object notation&#8217; code from an existing PowerShell object;  until now, that is. Phil Factor also produces a ConvertTo-YAML function and explains how they both work, with illustrative cod&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[4635,4871],"coauthors":[6813],"class_list":["post-1887","post","type-post","status-publish","format-standard","hentry","category-powershell","tag-powershell","tag-sysadmin"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1887","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1887"}],"version-history":[{"count":8,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1887\/revisions"}],"predecessor-version":[{"id":68565,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1887\/revisions\/68565"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1887"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1887"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1887"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1887"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}