Commands/Set-OpenXML.ps1

function Set-OpenXML
{
    <#
    .SYNOPSIS
        Sets OpenXML content
    .DESCRIPTION
        Sets content in an OpenXML file.
    .EXAMPLE
        Get-OpenXML ./Examples/Sample.docx |
            Set-OpenXML -Uri '/index.html' -Content ([xml]"<h1>Hello World</h1>") -ContentType text/html |
            Set-OpenXML -Uri '/404.html' -Content ([xml]"<h1>File Not Found</h1>") -ContentType text/html |
            Export-OpenXML ./Examples/Sample2.docx
    .LINK
        Get-OpenXML
    #>

    param(
    # The uri to set
    [Parameter(Mandatory,ParameterSetName='Uri',ValueFromPipelineByPropertyName)]
    [Alias('Url')]
    [uri]
    $Uri,

    # The content type. By default, `text/plain`
    [Parameter(ValueFromPipelineByPropertyName)]    
    [string]
    $ContentType = 'text/plain',

    # The content to set.
    [Parameter(ValueFromPipelineByPropertyName)]
    [PSObject]
    $Content,

    # The input object.
    # This must be a package, and it must be writeable.
    [Parameter(ValueFromPipeline)]
    [PSObject]
    $InputObject
    )

    process {
        # If there is no input, there is nothing to do
        if (-not $InputObject) { return }
        # If the input is not a package, pass it thru.
        if ($InputObject -isnot [IO.Packaging.Package]) {
            return $InputObject
        }

        # If the uri is not prefixed,
        if ($uri -notmatch '^/') {
            $uri = "/$uri" # add it to avoid easy errors.
        }

        # Get or create the part
        $part = 
            if ($InputObject.PartExists($uri)) {
                $InputObject.GetPart($uri)
            } else {
                $InputObject.CreatePart($uri, $ContentType)
            }

        if (-not $?) { return }

        # Get the stream
        $partStream = $part.GetStream()
        # First see if the content is a byte[]
        if ($content -is [byte[]]) {
            # if so, just write it
            $partStream.Write($content, 0, $content.Length)
        }
        # If the content is a stream,
        elseif ($content -is [IO.Stream]) {
            # copy it in.
            $content.CopyTo($partStream)
        }
        # If the content was xml or could be,
        elseif ($content -is [xml] -or ($contentXml = $content -as [xml])) {            
            if ($contentXml) { $content = $contentXml }              
            $buffer = $OutputEncoding.GetBytes($content.OuterXml)
            # write it to the package.
            $partStream.Write($buffer, 0, $buffer.Length)
        } elseif ($content -is [string]) {
            # Put strings in as a byte array.
            $buffer = $OutputEncoding.GetBytes($content)
            $partStream.Write($buffer, 0, $buffer.Length)
        } elseif ($contentBytes = $content -as [byte[]]) {
            # Bytes are obviously a byte array
            $partStream.Write($contentBytes, 0, $contentBytes.Length)
        } 
        elseif ($ContentType -match '[/\+]json') {
            # Explicitly typed json can be converted to json
            $buffer = $OutputEncoding.GetBytes((ConvertTo-Json -InputObject $content -Depth 10))
            $partStream.Write($buffer, 0, $buffer.Length)
        }
        else {
            # and everything else is stringified
            $buffer = $OutputEncoding.GetBytes("$content")
            $partStream.Write($buffer, 0, $buffer.Length)
        }

        # Close the part stream
        $partStream.Close()

        # and invalidate the parts cache on the object
        $inputObject.PSObject.Properties.Remove('.Parts')
        # then pass it thru so we can keep piping.
        $inputObject
    }
}