Fun With PowerShell – Opening Websites with Start-Process

Introduction

As part of my ArcaneBooks Project I described how to use the OpenLibrary Simple API to get book data.

In that post I also showed a way to bring up the webpage for an ISBN. I had a thought, why not build a function to add to the module to do that? This way a user would have an easy way to compare the output of the web API call to what the site holds.

In this post I’ll describe how to use the Start-Process cmdlet to open a target webpage.

Show-ISBNBookData

I created a new advanced function and named it Show-ISBNBookData. Here is the opening of the function.

function Show-ISBNBookData
{
  [CmdletBinding(HelpURI="https://github.com/arcanecode/ArcaneBooks/blob/1ebe781951f1a7fdf19bb6731487a74fa12ad08b/ArcaneBooks/Help/Get-ISBNBookData.md")]
  [alias("sisbn")]
  param (
         [Parameter( Mandatory = $true,
                     ValueFromPipeline = $true,
                     HelpMessage = 'Please enter the ISBN.'
                     )]
         [string] $ISBN
        )

If you want to learn more about advanced functions, see my post Fun With PowerShell – Advanced Functions. Briefly, the CmdletBinding attribute will turn this into an advanced function. Advanced functions allow you to input one or more parameters via the pipeline.

It has one parameter, the ISBN number you want to find. This can be passed in normally, or via the pipeline.

The Process Loop

In order to process multiple items from the pipeline you must enclose the heart of the function inside a process { } block. The process block is called once for each item passed in via the pipeline.

I then use the Replace method of the string object to remove any dashes or spaces from the ISBN that was passed in. This is then combined with the base OpenLibrary URL to create a new string, $url.

  process
  {
    foreach($number in $ISBN)
    {
      Write-Verbose "Beginning Show-ISBNBookData for $ISBN at $(Get-Date).ToString('yyyy-MM-dd hh:mm:ss tt')"

      $isbnFormatted = $ISBN.Replace('-', '').Replace(' ', '')
      $baseURL = "https://openlibrary.org/isbn/"

      $url = "$($baseURL)$($isbnFormatted)"

      Write-Verbose 'Opening the Book on OpenLibrary'

      Start-Process $url

      Write-Verbose "Finished Getting Data for $($ISBN)"
    }

The magic comes in the Start-Process cmdlet. This cmdlet analyzes the string that was passed in. It then looks for the default application for it, and attempts to open the associated application for the passed in string.

As an example, if you were to pass in the name of a Microsoft Word document, Start-Process would open Microsoft Word with the document name you passed in.

In this case, passing in a URL will attempt to open up your default web browser to the page you passed in.

If you called Show-ISBNBookData using the pipeline, the function will attempt to open up a new tab in your browser for each URL passed in via the pipeline.

Note I also used several Write-Verbose commands, you can learn more about it at Fun With PowerShell – Write-Verbose.

An Example

Calling the function is very simple.

$ISBN = '0-87259-481-5'
Show-ISBNBookData -ISBN $ISBN -Verbose

This should open up the following webpage in your default browser.

https://openlibrary.org/books/OL894295M/Your_HF_digital_companion

This is a reference to the book You HF Digital Companion.

See Also

You may find more helpful information at the links below.

ArcaneBooks Project

Fun With PowerShell – Advanced Functions

Fun With PowerShell – Strings

Fun With PowerShell – Write-Verbose

OpenLibrary Simple API

Conclusion

As you can see, Start-Process is extremely easy to use. Just pass in a URL or the name of a file, and PowerShell will attempt to open the item using the default application assigned in the operating system. In the ArcaneBooks project I’m using it to open a website, but you can use it for a variety of purposes.

If you like PowerShell, you might enjoy some of my Pluralsight courses. PowerShell 7 Quick Start for Developers on Linux, macOS and Windows is one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

Advertisement

Fun With PowerShell – Authoring Help

Introduction

Having good help is vital to the construction of a module. It explains not only how to use a function, but the purpose of the module and even more.

Naturally I’ve included good help text in the ArcaneBooks module, but as I was going over the construction of the ArcaneBooks module I realized I’d not written about how to write help in PowerShell. So in this post and the next I’ll address this very topic.

Two Types of Help

There are two ways of creating help for functions in PowerShell modules. The newer method is to create XML files with the help text. I’ll be honest, I’m not a big fan of this method.

The XML is more difficult to author and read in plain text format as the help is surrounded by XML tags. To be able to effectively author it a third party tool is needed.

There is one advantage to the XML format, if you wish to internationalize your module you can write individual XML help files for each language you need. These can all be bundled with your module. In my case I’m only going to use English, so this isn’t of benefit to my ArcaneBooks module.

I’ll admit that I may be a bit old fashioned, but I still prefer the original comment based help when authoring help. It keeps the help text with the function, and is easier to read when looking at the raw code.

Comment Blocks

As its name implies, comment based help is created by placing specially crafted comment blocks beside the function declarations of the functions in your module.

As you may know, a normal comment in PowerShell begins with a #, commonly called a pound sign or hash tag. Some examples:

# This is a comment

$x = 1  # Set X equal to 1

A comment block allows you to create comments that are multiple lines. They begin with a <# and end with #>. An example would be:

<#
Here is a comment block

More text here
#>

You can add text after and before the # characters. I often use these to creeate dividers in my code.

<#-----------------------------------------------
  Do some interesting stuff in this section
-----------------------------------------------#>

I’ll dive a bit deeper into the structure of the comment help block, but first lets talk about placement.

Placement of Comment Help

To associate a help block with a function, it needs to be positioned right before or right after the function declaration.

<#
Comment based help here
#>
function DoSomething()
function DoSomething()
<#
Comment based help here
#>

$x = 1

Either of these are valid, but I much prefer the first version. It keeps the function declaration close to its code.

Contents of Comment Based Help

There is a defined template of what needs to be in comment based help.

<#
.SYNOPSIS
A short one liner that describes the function

.DESCRIPTION
Detailed description of the function

.PARAMETER ParamName
Information about the parameter.

Add additional .PARAMETER tags for more parameters

.INPUTS
What inputs are allowed, useful for when a function allows input to be piped in.

.OUTPUTS
Explanation of what the function outputs.

Can also include sample data

.EXAMPLE
Code example

.EXAMPLE
Additional examples, just add more .EXAMPLE tags as needed

.NOTES
Notes here like author name

.LINK
Link to online help

.LINK
Additional link(s)
#>

As you can see, it uses a series of tags to describe what is in the section. Each tag is preceded by a period.

The SYNOPSIS and DESCRIPTION are both required. In the synopsis you place a short description of the function. One, no more than two sentences go here.

In the description you can place an expanded explanation of the function. You can go into detail of its purpose. It doesn’t need to be a novel, but two to three paragraphs are not uncommon.

Next comes the parameters. Each parameter should be listed individually, getting a PARAMETER tag followed by the name of the parameter. In the accompanying text you can include details to the nature of the parameter, whether it is required, and if appropriate the data type.

Again, you should include one parameter tag for each of your functions parameters.

In the INPUTS area you can give an overall description of the data that will be input to the function. It is also a good place to describe data that can be input to the function through the pipeline.

The OUTPUTS is the place to describe what data is returned from the function. This may be a single value, or an object with multiple values. When returning an object I like to list each property along with a sample value for each.

You should include at least one EXAMPLE section in your help. Include a small code sample of calling your function.

It’s a good idea though to include multiple example sections. For instance, if your function allows for input through the pipeline, have one example for passing data in normally, than a second for using the pipeline. Include as many as you need to give the reader a good set of examples on how to use your function.

NOTES is for just what it says, an area to include any additional notes about the function. In here I often include information such as the author name, copyright notices, and any other information I’d like to have included.

Finally is the LINK section. If you have online help, the first link tag should point to the online help web address that will be used with the -Online switch of the Get-Help cmdlet. You can include as many links as needed, I usually include at least one more pointing to the project website, such as a github site, or back to my own blog.

A Real World Example

Here is a real world example from the ArcaneBooks project I’ve been developing. This is the help for the Get-ISBNBookData function.

<#
.SYNOPSIS
Gets book data from OpenLibrary.org based on the ISBN

.DESCRIPTION
Uses the more advanced API at OpenLibrary to retrieved detailed information
based on the 10 or 13 character ISBN passed in.

.PARAMETER ISBN
A 10 or 13 digit ISBN number. The passed in value can have spaces or dashes,
it will remove them before processing the request to get the book data.

.INPUTS
Via the pipeline this cmdlet can accept an array of ISBN values.

.OUTPUTS
The cmdlet returns one or more objects of type Class ISBNBook with the
following properties. Note that not all properties may be present, it
depends on what data the publisher provided.

ISBN | The ISBN number that was passed in, complete with an formatting
ISBN10 | ISBN as 10 digits
ISBN13 | ISBN in 13 digit format
Title | The title of the book
LCCN | Library of Congress Catalog Number
Author | The author(s) of the book
ByStatement | The written by statement provided by the publisher
NumberOfPages | Number of pages in the book
Publishers | The Publisher(s) of this book
PublishDate | The publication date for this edition of the book
PublisherLocation | The location of the publisher
Subject | Generic subject(s) for the work
LibraryOfCongressClassification | Specialized classification used by Library of Congress
DeweyDecimalClass | Dewey Decimal number
Notes | Any additional information provided by the publisher
CoverUrlSmall | URL link to an image of the book cover, in a small size
CoverUrlMedium | URL link to an image of the book cover, in a medium size
CoverUrlLarge | URL link to an image of the book cover, in a large size

.EXAMPLE
# Pass in a single ISBN as a parameter
$ISBN = '0-87259-481-5'
$bookData = Get-ISBNBookData -ISBN $ISBN
$bookData

.EXAMPLE
# Pipe in a single ISBN
$ISBN = '0-87259-481-5'
$bookData = $ISBN | Get-ISBNBookData
$bookData

.EXAMPLE
# Pipe in an array of ISBNs
$ISBNs = @( '0-87259-481-5'
          , '0-8306-7801-8'
          , '0-8306-6801-2'
          , '0-672-21874-7'
          , '0-07-830973-5'
          , '978-1418065805'
          , '1418065803'
          , '978-0-9890350-5-7'
          , '1-887736-06-9'
          , '0-914126-02-4'
          , '978-1-4842-5930-6'
          )
$bookData = $ISBNs | Get-ISBNBookData -Verbose
$bookData

$bookData | Select-Object -Property ISBN, Title

.NOTES
ArcaneBooks - Get-ISBNBookData.ps1

Author: Robert C Cain | @ArcaneCode | arcane@arcanetc.com

This code is Copyright (c) 2023 Robert C Cain All rights reserved

The code herein is for demonstration purposes.
No warranty or guarantee is implied or expressly granted.

This module may not be reproduced in whole or in part without
the express written consent of the author.

.LINK
https://github.com/arcanecode/ArcaneBooks/blob/1ebe781951f1a7fdf19bb6731487a74fa12ad08b/ArcaneBooks/Help/Get-ISBNBookData.md

.LINK
http://arcanecode.me
#>

When I use the command Get-Help Get-ISBNBookData -Full this is the output.

SYNTAX
    Get-ISBNBookData [-ISBN] <String> [<CommonParameters>]


DESCRIPTION
    Uses the more advanced API at OpenLibrary to retrieved detailed information
    based on the 10 or 13 character ISBN passed in.


PARAMETERS
    -ISBN <String>
        A 10 or 13 digit ISBN number. The passed in value can have spaces or dashes,
        it will remove them before processing the request to get the book data.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS
    Via the pipeline this cmdlet can accept an array of ISBN values.


OUTPUTS
    The cmdlet returns one or more objects of type Class ISBNBook with the
    following properties. Note that not all properties may be present, it
    depends on what data the publisher provided.

    ISBN | The ISBN number that was passed in, complete with an formatting
    ISBN10 | ISBN as 10 digits
    ISBN13 | ISBN in 13 digit format
    Title | The title of the book
    LCCN | Library of Congress Catalog Number
    Author | The author(s) of the book
    ByStatement | The written by statement provided by the publisher
    NumberOfPages | Number of pages in the book
    Publishers | The Publisher(s) of this book
    PublishDate | The publication date for this edition of the book
    PublisherLocation | The location of the publisher
    Subject | Generic subject(s) for the work
    LibraryOfCongressClassification | Specialized classification used by Library of Congress
    DeweyDecimalClass | Dewey Decimal number
    Notes | Any additional information provided by the publisher
    CoverUrlSmall | URL link to an image of the book cover, in a small size
    CoverUrlMedium | URL link to an image of the book cover, in a medium size
    CoverUrlLarge | URL link to an image of the book cover, in a large size


NOTES


        ArcaneBooks - Get-ISBNBookData.ps1

        Author: Robert C Cain | @ArcaneCode | arcane@arcanetc.com

        This code is Copyright (c) 2023 Robert C Cain All rights reserved

        The code herein is for demonstration purposes.
        No warranty or guarantee is implied or expressly granted.

        This module may not be reproduced in whole or in part without
        the express written consent of the author.

    -------------------------- EXAMPLE 1 --------------------------

    PS > # Pass in a single ISBN as a parameter
    $ISBN = '0-87259-481-5'
    $bookData = Get-ISBNBookData -ISBN $ISBN
    $bookData






    -------------------------- EXAMPLE 2 --------------------------

    PS > # Pipe in a single ISBN
    $ISBN = '0-87259-481-5'
    $bookData = $ISBN | Get-ISBNBookData
    $bookData






    -------------------------- EXAMPLE 3 --------------------------

    PS > # Pipe in an array of ISBNs
    $ISBNs = @( '0-87259-481-5'
              , '0-8306-7801-8'
              , '0-8306-6801-2'
              , '0-672-21874-7'
              , '0-07-830973-5'
              , '978-1418065805'
              , '1418065803'
              , '978-0-9890350-5-7'
              , '1-887736-06-9'
              , '0-914126-02-4'
              , '978-1-4842-5930-6'
              )
    $bookData = $ISBNs | Get-ISBNBookData -Verbose
    $bookData

    $bookData | Select-Object -Property ISBN, Title





RELATED LINKS
    https://github.com/arcanecode/ArcaneBooks/blob/1ebe781951f1a7fdf19bb6731487a74fa12ad08b/ArcaneBooks/Help/Get-ISBNBookData.md
    http://arcanecode.me

See Also

The ArcaneBooks Project – An Introduction

Conclusion

As you can see, implementing comment based help is quite easy. It’s also important, as users rely on help to understand how to use the functions you author. You’ll also find it helpful as a reminder to yourself about the functionality of your own code down the road.

Another useful feature for help is to create about_ help for your modules. You’ve likely seen these before, Microsoft provides a long list of about topics for PowerShell itself.

You can create your own set of about help for your module, and in the next post I’ll show you how.

ArcaneBooks – Parsing Library of Congress Control Number (LCCN) Data With PowerShell

Introduction

In my previous post in this series, ArcaneBooks – Library of Congress Control Number (LCCN) – An Overview, I provided an overview of the LCCN and the basics of calling its public web API to retrieve data based on the LCCN.

In this post I will demonstrate how to call the API and dissect the data using PowerShell. This will be a code intensive post.

You can find the full ArcaneBooks project on my GitHub site. Please note as of the writing of this post the project is still in development.

The code examples for this post can be located at https://github.com/arcanecode/ArcaneBooks/tree/main/Blog_Posts/005.00_LCCN_API. It contains the script that we’ll be dissecting here.

XML from Library of Congress

For this demo, we’ll be using an LCCN of 54-9698, Elements of radio servicing by William Marcus. When we call the web API URL in our web browser, we get the following data.

<zs:searchRetrieveResponse xmlns:zs="http://docs.oasis-open.org/ns/search-ws/sruResponse">
  <zs:numberOfRecords>2</zs:numberOfRecords>
  <zs:records>
    <zs:record>
      <zs:recordSchema>mods</zs:recordSchema>
      <zs:recordXMLEscaping>xml</zs:recordXMLEscaping>
      <zs:recordData>
        <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://www.loc.gov/mods/v3" version="3.8" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-8.xsd">
          <titleInfo>
            <title>Elements of radio servicing</title>
          </titleInfo>
          <name type="personal" usage="primary">
            <namePart>Marcus, William. [from old catalog]</namePart>
          </name>
          <name type="personal">
            <namePart>Levy, Alex,</namePart>
            <role>
              <roleTerm type="text">joint author</roleTerm>
            </role>
          </name>
          <typeOfResource>text</typeOfResource>
          <originInfo>
            <place>
              <placeTerm type="code" authority="marccountry">nyu</placeTerm>
            </place>
            <dateIssued encoding="marc">1955</dateIssued>
            <issuance>monographic</issuance>
            <place>
              <placeTerm type="text">New York</placeTerm>
            </place>
            <agent>
              <namePart>McGraw Hill</namePart>
            </agent>
            <dateIssued>[1955]</dateIssued>
            <edition>2d ed.</edition>
          </originInfo>
          <language>
            <languageTerm authority="iso639-2b" type="code">eng</languageTerm>
          </language>
          <physicalDescription>
            <form authority="marcform">print</form>
            <extent>566 p. illus. 24 cm.</extent>
          </physicalDescription>
          <subject authority="lcsh">
            <topic>Radio</topic>
            <topic>Repairing. [from old catalog]</topic>
          </subject>
          <classification authority="lcc">TK6553 .M298 1955</classification>
          <identifier type="lccn">54009698</identifier>
          <recordInfo>
            <recordContentSource authority="marcorg">DLC</recordContentSource>
            <recordCreationDate encoding="marc">820525</recordCreationDate>
            <recordChangeDate encoding="iso8601">20040824072855.0</recordChangeDate>
            <recordIdentifier>6046000</recordIdentifier>
            <recordOrigin>Converted from MARCXML to MODS version 3.8 using MARC21slim2MODS3-8_XSLT1-0.xsl (Revision 1.172 20230208)</recordOrigin>
          </recordInfo>
        </mods>
      </zs:recordData>
      <zs:recordPosition>1</zs:recordPosition>
    </zs:record>
  </zs:records>
  <zs:nextRecordPosition>2</zs:nextRecordPosition>
  <zs:echoedSearchRetrieveRequest>
    <zs:version>2.0</zs:version>
    <zs:query>bath.lccn=54009698</zs:query>
    <zs:maximumRecords>1</zs:maximumRecords>
    <zs:recordXMLEscaping>xml</zs:recordXMLEscaping>
    <zs:recordSchema>mods</zs:recordSchema>
  </zs:echoedSearchRetrieveRequest>
  <zs:diagnostics xmlns:diag="http://docs.oasis-open.org/ns/search-ws/diagnostic">
    <diag:diagnostic>
      <diag:uri>info:srw/diagnostic/1/5</diag:uri>
      <diag:details>2.0</diag:details>
      <diag:message>Unsupported version</diag:message>
    </diag:diagnostic>
  </zs:diagnostics>
</zs:searchRetrieveResponse>

Let’s see how to retrieve this data then parse it using PowerShell.

Parsing LCCN Data

First, we’ll start by setting the LCCN in a variable. This is the LCCN for "Elements of radio servicing" by William Marcus

$LCCN = '54-9698'

To pass in the LCCN to the web API, we need to remove any dashes or spaces.

$lccnCleaned = $LCCN.Replace('-', '').Replace(' ', '')

After 2001 the LCCN started using a four digit year. By that time however, books were already printing the ISBN instead of the LCCN. For those books we’ll be using the ISBN, so for this module we can safely assume the LCCNs we are receiving only have a two digit year.

With that said, we’ll use the following code to extract the two digit year.

$lccnPrefix = $lccnCleaned.Substring(0,2)

Since digits 0 and 1 are the year, we’ll start getting the rest of the LCCN at the third digit, which is in position 2 and go to the end of the string, getting the characters.

Next, the API requires the remaining part of the LCCN must be six digits. So we’ll use the PadLeft method to put 0’s in front to make it six digits.

$lccnPadded = $lccnCleaned.Substring(2).PadLeft(6, '0')

Now combine the reformatted LCCN and save it to a variable.

$lccnFormatted ="$($lccnPrefix)$($lccnPadded)"

Now we’ll combine all the parts to create the URL needed to call the web API.

$baseURL = "http://lx2.loc.gov:210/lcdb?version=3&operation=searchRetrieve&query=bath.lccn="
$urlParams = "&maximumRecords=1&recordSchema=mods"
$url = "$($baseURL)$($lccnFormatted)$($urlParams)"

It’s time now to get the LCCN data from the Library of Congress site. We’ll wrap it in a try/catch so in case the call fails, for example from the internet going down, it will provide a message and exit.

Note at the end of the Write-Host line we use the PowerShell line continuation character of ` (a single backtick) so we can put the foreground color on the next line, making the code a bit more readable.

try {
  $bookData = Invoke-RestMethod $url
}
catch {
  Write-Host "Failed to retrieve LCCN $LCCN. Possible internet connection issue. Script exiting." `
    -ForegroundColor Red
  # If there's an error, quit running the script
  exit
}

Now we need to see if the book was found in the archive. If not the title will be null. We’ll use an if to check to see if the LCCN was found in their database. If not, the title property will be null. If so we display a message to that effect.

If it was found, we fall through into the else clause to process the data. The remaining code resides within the else.

# We let the user know, and skip the rest of the script
if ($null -eq $bookData.searchRetrieveResponse.records.record.recordData.mods.titleInfo.title)
{
  Write-Host = "Retrieving LCCN $LCCN returned no data. The book was not found."
}
else # Great, the book was found, assign the data to variables
{

To get the data, we start at the root object, $bookData. The main node in the returned XML is searchRetrieveResponse. From here we can use standard dot notation to work our way down the XML tree to get the properties we want.

Our first entry gets the Library of Congress Number. The syntax is a little odd. If we walk XML tree, we find this stored in:

<identifier type="lccn">54009698</identifier>

If we display the identifier property using this code:

$bookData.searchRetrieveResponse.records.record.recordData.mods.identifier

We get this result.

type #text
---- -----
lccn 54009698

The LCCN we want is stored in the property named #text. But #text isn’t a valid property name in PowerShell. We can still use it though if we wrap the name in quotes.

  $LibraryOfCongressNumber = $bookData.searchRetrieveResponse.records.record.recordData.mods.identifier.'#text'

From here we can process other properties that are easy to access.

  $Title = $bookData.searchRetrieveResponse.records.record.recordData.mods.titleInfo.title
  $PublishDate = $bookData.searchRetrieveResponse.records.record.recordData.mods.originInfo.dateIssued.'#text'
  $LibraryOfCongressClassification = $bookData.searchRetrieveResponse.records.record.recordData.mods.classification.'#text'
  $Description = $bookData.searchRetrieveResponse.records.record.recordData.mods.physicalDescription.extent
  $Edition = $bookData.searchRetrieveResponse.records.record.recordData.mods.originInfo.edition

Now we get to the section where an XML property can contain one or more values.

Books can have multiple authors, each is returned in its own item in an array. One example is the book subjects. Here is a sample of the XML:

<subject authority="lcsh">
  <topic>Radio</topic>
  <topic>Repairing. [from old catalog]</topic>
</subject>

As you can see, this has two topics. What we need to do is retrieve the root, in this case subject, then loop over each item.

For our purposes we don’t need them individually, a single string will do. So in the PowerShell we’ll create a new object of type StringBuilder. For more information on how to use StringBuilder, see my post Fun With PowerShell – StringBuilder.

In the loop if the variable used to hold the string builder is empty, we’ll just add the first item. If it’s not empty, we’ll append a comma, then append the next value.

  $authors = [System.Text.StringBuilder]::new()
  foreach ($a in $bookData.searchRetrieveResponse.records.record.recordData.mods.name)
  {
    if ($a.Length -gt 1)
      { [void]$authors.Append(", $($a.namePart)") }
    else
      { [void]$authors.Append($a.namePart) }
  }
  $Author = $authors.ToString()

As a final step we used the ToString method to convert the data in the string builder back to a normal string and store it in the $Author variable.

From here, we’ll repeat this logic for several other items that can hold multiple values. The books subjects is one example.

  $subjects = [System.Text.StringBuilder]::new()
  $topics = $bookData.searchRetrieveResponse.records.record.recordData.mods.subject | Select topic
  foreach ($s in $topics.topic)
  {
    if ($subjects.Length -gt 1)
      { [void]$subjects.Append(", $($s)") }
    else
      { [void]$subjects.Append($s) }
  }
  $Subject = $subjects.ToString()

A book could have multiple publishers over time. The author could shift to a new publisher, or more likely a publishing house could be purchased and the new owners name used. The data is returned as an array, so combine them as we did with authors and subjects.

Note that in the returned data, the publisher is stored as an "agent". We’ll use the name Publisher to keep it consistent with the ISBN data.

  $thePublishers = [System.Text.StringBuilder]::new()
  foreach ($p in $bookData.searchRetrieveResponse.records.record.recordData.mods.originInfo.agent)
  {
    if ($thePublishers.Length -gt 1)
      { [void]$thePublishers.Append(", $($p.namePart)") }
    else
      { [void]$thePublishers.Append($p.namePart) }
  }
  $Publishers = $thePublishers.ToString()

Since there could be multiple publishers, logically there could be multiple publishing locations. This section will combine them to a single location.

  $locations = [System.Text.StringBuilder]::new()
  foreach ($l in $bookData.searchRetrieveResponse.records.record.recordData.mods.originInfo.place.placeTerm)
  {
    if ($locations.Length -gt 1)
      { [void]$locations.Append(", $($l.'#text')") }
    else
      { [void]$locations.Append($l.'#text') }
  }
  $PublisherLocation = $locations.ToString()

All done! We’ll give a success message to let the user know.

  Write-Host "Successfully retrieved data for LCCN $LCCN" -ForegroundColor Green

Finally, we’ll display the results. Note some fields may not have data, that’s fairly normal. The Library of Congress only has the data provided by the publisher. In addition some of the LCCN data dates back many decades, so the data supplied in the 1940’s may be different than what is supplied today.

  "LCCN: $LCCN"
  "Formatted LCCN: $lccnFormatted"
  "Library Of Congress Number: $LibraryOfCongressNumber"
  "Title: $Title"
  "Publish Date: $PublishDate"
  "Library Of Congress Classification: $LibraryOfCongressClassification"
  "Description: $Description"
  "Edition: $Edition"
  "Author: $Author"
  "Subject: $Subject"
  "Publishers: $Publishers"
  "Publisher Location: $PublisherLocation"
}

The Result

Here is the result of the above code.

LCCN: 54-9698
Formatted LCCN: 54009698
Library Of Congress Number: 54009698
Title: Elements of radio servicing
Publish Date: 1955
Library Of Congress Classification: TK6553 .M298 1955
Description: 566 p. illus. 24 cm.
Edition: 2d ed.
Author: Marcus, William. [from old catalog], Levy, Alex,
Subject: Radio, Repairing. [from old catalog]
Publishers: McGraw Hill
Publisher Location: nyu, New York

As you can see it returned a full dataset. Not all books my have data for all the fields, but this one had the full details on record with the Library of Congress.

See Also

This section has links to other blog posts or websites that you may find helpful.

The ArcaneBooks Project – An Introduction

ArcaneBooks – ISBN Overview, PowerShell, and the Simple OpenLibrary ISBN API

ArcaneBooks – PowerShell and the Advanced OpenLibrary ISBN API

ArcaneBooks – Library of Congress Control Number (LCCN) – An Overview

Fun With PowerShell – StringBuilder

The GitHub Site for ArcaneBooks

Conclusion

In this document we covered the basics of the LCCN as well as the web API provided by the Library of Congress. Understanding this information is important when we integrate the call into our PowerShell code.

ArcaneBooks – Library of Congress Control Number (LCCN) – An Overview

Introduction

This is part of my ongoing series on my ArcaneBooks project. The goal is to provide a module to retrieve book data via provided web APIs. In the SEE ALSO section later in this post I’ll provide links to previous posts which cover the background of the project, as well as how to use the OpenLibrary APIs to get data based on the ISBN.

In this post I will provide an overview of using the Library of Congress API to get data based on the LCCN, short for Library of Congress Control Number.

The next post in this series will provide code examples and an explanation of how to use PowerShell to get data using the Library of Congress API.

LCCN Overview

The abbreviation LCCN, according to the Library of Congress’s own website, stands for Library of Congress Control Number. When the system was first created in 1898, however, LCCN stood for Library of Congress Card Number, and I’ve seen it both ways in publications.

I’ve also seen a few places define it as Library of Congress Catalog Number, although this was never an official designation.

The LCCN was created in 1898 to provide a unique value to every item in the Library of Congress. This not only includes books, but works of art, manuscripts (not in book form), maps, and more.

LCCN Format

The LCCN has two parts, a prefix followed by a serial number. From 1898 to 2000 the prefix was two digits, representing the year. Beginning in 2001 the prefix became four digits, representing the year.

The serial number is simple a sequential number. 45-1 was the first number assigned in 1945. 45-1234 was the 1,234th item assigned in that year.

Be aware from 1969 to 1972 there was an experiment where the single digit of 7 was used for the prefix. They decided this scheme wasn’t going to work out, and reverted to the standard format of year followed by serial number.

Here are a few examples of real LCCNs from books in my personal collection. You can use these in your own testing.

LCCN Title
54-9698 Elements of Radio Servicing
40-33904 Radio Handbook Twenty-Second Edition
41-3345 The Radio Amateur’s Handbook 42nd Edition 1965
64-20875 Early Electrical Communication
74-75450 VHF Handbook for Radio Amateurs
76-190590 Wire Antennas for Radio Amateurs
71-120473 73 Vertical, Beam, and Triangle Antennas

Accessing Book Data from the Library of Congress

The Library of Congress actually provides two web APIs for getting book data. The first API is for accessing assets, such as digital assets. It doesn’t return much data for books.

The second is the LC Z39.50 system, accessible through lx2.loc.gov. Here is an example of calling it to retrieve a record for the book Elements of Radio Servicing, which has the LCCN of 54-9698. (It should, of course, all be used as a single line just in case your web browser wraps it.)

http://lx2.loc.gov:210/lcdb?version=3&amp;operation=searchRetrieve&amp;query=bath.lccn=54009698&amp;maximumRecords=1&amp;recordSchema=mods

Breaking it down, the root call is to http://lx2.loc.gov:210/lcdb. After this is a question mark ?, followed by the parameters.

The first parameter is version=3. This indicates which format to use for the return data. It supports two versions, 1.1 and 3. For our purposes we’ll use the most current version, 3.

Following the ampersand &amp; is operation=searchRetrieve. This instructs the Library of Congress’s API that we want to do a search to retrieve data.

Next is the core piece, we need to tell it what LCCN number to look up, query=bath.lccn=54009698. The root object is bath, then it uses the property lccn.

The LCCN has to be formatted in a specific way. We start with the two or four digit year. In the above example, 54-9698, this would be the two digit year of 54.

Next is the serial number. If the number is less than six digits, it must be left zero padded to become six. Thus 9698 becomes 009698. The year and serial number are combined, removing any dashes, spaces, or other characters and becomes 54009698.

Following is maximumRecords=1, indicating we only expect one record back. That’s all we’ll get back with a single LCCN anyway, so this will work fine for our needs.

The final parameter is recordSchema=mods. The API supports several formats.

Record Schema Description Notes
dc Dublin Core (bibliographic records) Brings back just the basics (Name, author, etc)
mads MADS (authority records) Brief, not a lot of info
mods MODS (bibliographic records) Very readable XML schema, most info
marcxml MARCXML – the default schema Abbreviated schema, not readable
opacxml MARCXML (wth holdings attached) As above with a bit more info

You are welcome to experiment with different formats, but for this module we’ll be using mods. It provides the most information, and is in XML. XML is very easy to read, and it works great with PowerShell.

ISBN and Library of Congress

It is possible to use the Library of Congress to look up the ISBN. In my testing though, the interface provided by OpenLibrary provided more data. Thus we’ll be using it for looking up ISBNs in this module.

We’ll use the LCCN API for books where we only have the LCCN.

See Also

The ArcaneBooks Project – An Introduction

ArcaneBooks – ISBN Overview, PowerShell, and the Simple OpenLibrary ISBN API

ArcaneBooks – PowerShell and the Advanced OpenLibrary ISBN API

Conclusion

In this document we covered the basics of the LCCN as well as the web API provided by the Library of Congress. Understanding this information is important when we integrate the call into our PowerShell code.

ArcaneBooks – PowerShell and the Advanced OpenLibrary ISBN API

Introduction

This post continues my series on my ArcaneBooks project. For a background see my post The ArcaneBooks Project – An Introduction.

For this project I am using the OpenLibrary.org website, which provides two web APIs to access book data based on the ISBN. OpenLibrary is sponsored by the InternetArchive.

In a previous post, ArcaneBooks – ISBN Overview, PowerShell, and the Simple OpenLibrary ISBN API, I covered the use of the first API which I nicknamed the Simple API as it is a bit easier to use and dissect the results. I also provided a background on what the ISBN is and how it is formed.

In this post I’ll dive into the more complex of the APIs, what I call the Advance API.

Be aware the use of Simple and Advance are my terms, so I can easily distinguish between the two. They are not terms used by the OpenLibrary.

The Advanced OpenLibrary API

The format of the Advanced API is slightly different from the simple. Here is template.

https://openlibrary.org/api/books?bibkeys=ISBN:[ISBN Goes Here]&jscmd=data&format=json"

You will replace the [ISBN Goes Here] text with the ISBN number you want to look up. Be aware this can only be digits, you must remove any spaces, dashes, or other characters.

Let’s look at a code example of calling the API and getting all its properties.

Calling The API with PowerShell

First, set an ISBN to lookup. We’ll include some dashes for the demo. The title of the book is "Your HF Digital Companion"

$ISBN = '0-87259-481-5'

Now remove any spaces or dashes, then create the URL.

$isbnFormatted = $ISBN.Replace('-', '').Replace(' ', '')
$baseURL = "https://openlibrary.org/api/books?bibkeys=ISBN:"
$urlParams = "&jscmd=data&format=json"
$url = "$($baseURL)$($isbnFormatted)$($urlParams)"

Now let’s call the URL and put the data into a variable.

$bookData = Invoke-RestMethod $url

If we look at the data held by the variable, we get back a single column. That column holds JSON formatted data. (Note I truncated the XML for readability purposes.)

$bookData

This is the output of displaying the variable.

ISBN:0872594815
---------------
@{url=https://openlibrary.org/books/OL894295M/Your_HF_digital_companion; key=/books/OL894295M; title=Your HF digital companion; authors=System.Object[]; number_of_pages=197; …

We could address the data like:

$bookData.'ISBN:0872594815'.Title

Note we had to wrap the ISBN number in quotes since a colon : isn’t an allowed in property names. However, when we make the call the ISBN isn’t set in stone.

But we do have it in a variable, and we can use string interpolation to format the property.

$bookData."ISBN:$isbnformatted".title

This returns "Your HF digital companion". And yes, the words "digital" and "companion" should normally be capitalized, but this is the way the title comes from OpenLibrary.

Now that we have the formatting for the property name down, we can get the other properties. Note that not all properties that are returned will have data.

$ISBN10 = $bookData."ISBN:$isbnformatted".identifiers.isbn_10
$ISBN13 = $bookData."ISBN:$isbnformatted".identifiers.isbn_13
$Title = $bookData."ISBN:$isbnformatted".title
$LCCN = $bookData."ISBN:$isbnformatted".identifiers.lccn
$NumberOfPages = $bookData."ISBN:$isbnformatted".number_of_pages
$PublishDate = $bookData."ISBN:$isbnformatted".publish_date
$LibraryOfCongressClassification = $bookData."ISBN:$isbnformatted".classifications.lc_classifications
$DeweyDecimalClass = $bookData."ISBN:$isbnformatted".classifications.dewey_decimal_class
$Notes = $bookData."ISBN:$isbnformatted".notes
$CoverUrlSmall = $bookData."ISBN:$isbnformatted".cover.small
$CoverUrlMedium = $bookData."ISBN:$isbnformatted".cover.medium
$CoverUrlLarge = $bookData."ISBN:$isbnformatted".cover.large

The ByStatement sometimes begins with the word "By ". If so we want to remove it. However if we try and do a replace and the by_statement column is null, attempting to call the Replace method will result in an error. So first we have to check for null, and only if the by_statement isn’t null do we attempt to do a replace.

if ($null -eq $bookData."ISBN:$isbnformatted".by_statement)
  { $ByStatement = '' }
else
  { $ByStatement = $bookData."ISBN:$isbnformatted".by_statement.Replace('by ', '') }

For the remaining data, each item can have multiple entries attached. For example, a book could have multiple authors. For our purposes we will just combine into a single entry.

We’ll create a new variable of type StringBuilder, then loop over the list of items in the JSON, combining them into a single string.

In the if, we check to see if the string already has data, if so we append a comma before adding the second (or more) authors name.

Finally we use the ToString method of the StringBuilder class to convert the value back into a standard string data type.

Books can have multiple authors, as stated each is returned in its own item in an array. This code will combine them into a single string.

Note that when we call the Append method of the StringBuilder class, we need to prepend it with [void], otherwise it will send output to the console which we don’t want.

$authors = [System.Text.StringBuilder]::new()
foreach ($a in $bookData."ISBN:$isbnformatted".authors)
{
  if ($authors.Length -gt 1)
    { [void]$authors.Append(", $($a.name)") }
  else
    { [void]$authors.Append($a.name) }
}
$Author = $authors.ToString()

Subjects can be an array, let’s combine them into a single string.

$subjects = [System.Text.StringBuilder]::new()
foreach ($s in $bookData."ISBN:$isbnformatted".subjects)
{
  if ($subjects.Length -gt 1)
    { [void]$subjects.Append(", $($s.name)") }
  else
    { [void]$subjects.Append($s.name) }
}
$Subject = $subjects.ToString()

A book could have multiple publishers over time. The author could shift to a new publisher, or more likely a publishing house could be purchases and the new owners name used. The data is returned as an array, so combine them as we did with authors and subjects.

$thePublishers = [System.Text.StringBuilder]::new()
foreach ($p in $bookData."ISBN:$isbnformatted".publishers)
{
  if ($thePublishers.Length -gt 1)
    { [void]$thePublishers.Append(", $($p.name)") }
  else
    { [void]$thePublishers.Append($p.name) }
}
$Publishers = $thePublishers.ToString()

Since there could be multiple publishers, logically there could be multiple publishing locations. This will combine them into a single string.

$locations = [System.Text.StringBuilder]::new()
foreach ($l in $bookData."ISBN:$isbnformatted".publish_places)
{
  if ($locations.Length -gt 1)
    { [void]$locations.Append(", $($l.name)") }
  else
    { [void]$locations.Append($l.name) }
}
$PublisherLocation = $locations.ToString()

Now print out all the returned data.

$ISBN10
$ISBN13
$Title
$LCCN
$NumberOfPages
$PublishDate
$LibraryOfCongressClassification
$DeweyDecimalClass
$Notes
$CoverUrlSmall
$CoverUrlMedium
$CoverUrlLarge
$ByStatement
$Author
$Subject
$Publishers
$PublisherLocation

The Output

Here is the output, I put it into a table for easier reading.

Item Value
ISBN10 95185134
ISBN13 [Missing Value]
Title Your HF digital companion
LCCN [Missing Value]
Number of Pages 197
Publish Date 1995
LibraryOfCongressClassification TK5745 .F572 1995
DeweyDecimalClass 004.6/4
Notes Includes bibliographical references.
Based on Your RTTY/AMTOR companion. 1st ed. c1993.
CoverUrlSmall https://covers.openlibrary.org/b/id/12774631-S.jpg
CoverUrlMedium https://covers.openlibrary.org/b/id/12774631-M.jpg
CoverUrlLarge https://covers.openlibrary.org/b/id/12774631-L.jpg
ByStatement Steve Ford.
Author Steve Ford
Subject Radiotelegraph, Amateurs’ manuals
Publishers American Radio Relay League
PublisherLocation Newington, CT

See Also

The following operators and functions were used or mentioned in this article’s demos. You can learn more about them in some of my previous posts, linked below.

Fun With PowerShell Logic Branching

Fun With PowerShell Loops

Fun With PowerShell Strings

Fun With PowerShell String Formatting

Conclusion

In this post we saw how to use what I call the advanced API offered by OpenLibrary to retrieve book data based on the ISBN.

In the next post we’ll see how to get book data based on the Library Of Congress Catalog Number, using PowerShell and the Library of Congresses web API.

The demos in this series of blog posts was inspired by my Pluralsight course PowerShell 7 Quick Start for Developers on Linux, macOS and Windows, one of many PowerShell courses I have on Pluralsight. All of my courses are linked on my About Me page.

If you don’t have a Pluralsight subscription, just go to my list of courses on Pluralsight . At the top is a Try For Free button you can use to get a free 10 day subscription to Pluralsight, with which you can watch my courses, or any other course on the site.

ArcaneBooks – ISBN Overview, PowerShell, and the Simple OpenLibrary ISBN API

Introduction

In this post we’ll begin with an overview of what an ISBN is. We’ll then talk about the website that will be the source for our data. It has two different web APIs (Application Programming Interface) that we can use. We’ll discuss one here, then in the next blog post cover the advanced version.

First though, if you haven’t read the introductory post in this series, The ArcaneBooks Project – An Introduction, I’d highly recommend doing so as it lays the foundation for why and how this project to get ISBN data originated.

ISBN Overview

ISBN, or International Standard Book Number, is a 10 or 13 digit number used to uniquely identify a book. You can find more information on ISBNs at https://www.isbn.org.

Every country has a service that creates an ISBN number for works created in that country, and cannot create numbers for books in other countries. ISBN’s began in 1970 as a ten digit number. In 2007, due to a dwindling supply of numbers, it was switched to a thirteen digit number.

Thirteen digit numbers in the US currently begin with 978-0 and 978-1, and 979-8. For purposes of this module, though, we will be able to use either the ten or thirteen number version of the ISBN.

Publishers sometimes vary the format through the inclusion or exclusion of dashes and/or spaces to separate the parts of the ISBN. For our purposes this won’t be relevant.

The data source we use (more on that momentarily) requires us to use only the number with no spaces or dashes. Thus the cmdlet in this module to fetch the ISBN data will remove any spaces or dashes from the number before it is used.

Converting Between Ten and Thirteen Characters

It is possible, although not necessary for this module, to convert the ten digit ISBN to a thirteen digit one. The https://www.isbn.org website has an online converter which you’ll find at https://www.isbn.org/ISBN_converter.

There is also a good blog article at The Postulate. In it the author gives an overview of how to convert from ten to thirteen character ISBNs, and provides code samples in Python.

Again, since our source can use either format of the ISBN, there are no plans at this time to implement the routine as PowerShell code.

Data Source – The Simple OpenLibrary API

In the ArcaneBooks PowerShell module that we are building, we will use the OpenLibrary.org API to get our data. The OpenLibrary website is run by the Internet Archive.

As I mentioned in the introduction, OpenLibrary actually has two different APIs we can call. The first I call the Simple API as it is very easy to use and parse the data.

The second API I’ve called the Advanced version, as it returns much more data, but the returned JSON is more complex to parse.

Note the words Simple and Advanced are my words to describe their APIs, not OpenLibrary’s. I just needed an easy way to distinguish between the two.

For our ArcaneBooks module we’ll be using the Advanced version as it contains extra data we need for our project. That may not be the case for you, so in this post I’ll cover how to use the Simple version. The next post will dive into the more complex Advanced API.

The OpenLibrary Simple API works in one of two ways. Whichever mode you choose, any dashes, spaces, etc must be removed before calling their URLs. Only a string of ten or thirteen numbers can be passed in.

In the first method, you can return an HTML webpage with the book data nicely formatted. The URL (Uniform Resource Locater, a fancy way of saying the web address) to access this is formatted like:

https://openlibrary.org/isbn/[isbn]

Where [isbn] is replaced by the ten or thirteen character ISBN number.

https://openlibrary.org/isbn/0672218747

Will bring up the book data for William Orr’s The Radio Handbook.

We can also return the data in JSON format. To get the data in JSON, all we have to do is append .json to the url. This url will return the same book in JSON format.

https://openlibrary.org/isbn/0672218747.json

The OpenLibrary is flexible, you can use either the ten or thirteen digit number in the call. Both of these examples will bring up the same book, Master Handbook of Ham Radio Circuits by Robert J. Traister.

https://openlibrary.org/isbn/0830678018.json
https://openlibrary.org/isbn/9780830678013.json

The Output JSON

Below is an example of the JSON output (formatted) as returned by the simple API when we call it using the call of https://openlibrary.org/isbn/0672218747.json.

{
  "publishers": [
    "Sams"
  ],
  "languages": [
    {
      "key": "/languages/eng"
    }
  ],
  "identifiers": {
    "goodreads": [
      "3084620"
    ],
    "librarything": [
      "1522789"
    ]
  },
  "title": "Radio Handbook",
  "physical_format": "Hardcover",
  "number_of_pages": 1168,
  "isbn_13": [
    "9780672218743"
  ],
  "isbn_10": [
    "0672218747"
  ],
  "publish_date": "December 1982",
  "key": "/books/OL7667922M",
  "authors": [
    {
      "key": "/authors/OL1196498A"
    }
  ],
  "works": [
    {
      "key": "/works/OL8273120W"
    }
  ],
  "type": {
    "key": "/type/edition"
  },
  "subjects": [
    "Radio",
    "Technology & Industrial Arts"
  ],
  "latest_revision": 6,
  "revision": 6,
  "created": {
    "type": "/type/datetime",
    "value": "2008-04-29T15:03:11.581851"
  },
  "last_modified": {
    "type": "/type/datetime",
    "value": "2022-02-16T09:26:53.493088"
  }
}

Parsing the Returned Data

Below is PowerShell code for accessing the data from OpenLibrary. We call the Simple API using Invoke-RestMethod, then parse its various properties.

For an explanation of what is happening, see the comments in the PowerShell sample code. Note that the sample code doesn’t access every property returned, just enough of them to show you how to deal with the various ways the returned JSON is formatted.


# Set the URL and call the API via Invoke-RestMethod
# The returned JSON data will be held in the variable $BookData
$url = 'https://openlibrary.org/isbn/0672218747.json'
$bookData = Invoke-RestMethod $url

# Here is an extract of part of the JSON code.
# Below we'll show how to access it with PowerShell

<#
   ...more json here
   "title": "Radio Handbook",
   "number_of_pages": 1168,
   ...more json follows
#>

# Here we can just use the variable holding the returned JSON,
# then dot notation to access the property we want
# We're also using string interpolation. Because we are accessing
# properties of an object, we have to wrap it in $()
"Title: $($bookData.title)"
"Number Of Pages: $($bookData.number_of_pages)"

# Identifiers is a JSON object with two values, goodreads and librarything

<#
   ...more json here
   "identifiers": {
     "goodreads": [
       "3084620"
     ],
     "librarything": [
       "1522789"
     ]
   },
   ...more json follows
#>

# We can get the data by just using additional dot notation after the
# identifiers object
"GoodReads Number: $($bookData.identifiers.goodreads)"
"LibraryThing Number: $($bookData.identifiers.librarything)"


# Subjects is returned as a JSON array within the Subjects property

<#
   ...more json here
  "subjects": [
    "Radio",
    "Technology & Industrial Arts"
  ],
   ...more json follows
#>

# You can deal with these in two ways.
# First, you can list individually
foreach ($s in $bookData.subjects)
{
  "Subject: $s"
}

# In the second method you could combine in a single string

# Start by creating a StringBuilder object to append the strings
# as efficiently as possible
$mySubjects = [System.Text.StringBuilder]::new()

# Loop over the BookData Subjects array, copying the current
# subject into the variable $s
foreach ($s in $bookData.subjects)
{
  if ($mySubjects.Length -gt 1)
  {
    # If we already have data in our StringBuilder $mySubjects,
    # then add a comma first, then the name of the subject from $s
    [void]$mySubjects.Append(", $($s)")
  }
  else
  {
    # If the length of our StringBuilder variable is less than 1,
    # it's empty so we'll just append the name of the subject without
    # a comma in front
    [void]$mySubjects.Append($s)
  }
}

# We need to convert the StringBuilder to a normal String to
# use it effectively, for example as a property in a class
$myCombinedSubjects = $mySubjects.ToString()
"Combined Subjects: $myCombinedSubjects"

# The created and last_modified properties are objects,
# simliar to the identifiers

<#
   ...more json here
  "created": {
    "type": "/type/datetime",
    "value": "2008-04-29T15:03:11.581851"
  },
  "last_modified": {
    "type": "/type/datetime",
    "value": "2022-02-16T09:26:53.493088"
  }
   ...more json follows
#>

# For both of these we only need the value property, so we can
# just ignore the type
"Created: $($bookData.created.value)"
"Last Modified: $($bookData.last_modified.value)"

Sample Data

Here are some sample ISBNs you can use for testing the sample code.

ISBN Title
0-87259-481-5 Your HF Digital Companion
0-8306-7801-8 Master Handbook of Ham Radio Circuits (Hardback)
0-8306-6801-2 Master Handbook of Ham Radio Circuits (Paperback)
0-672-21874-7 Radio Handbook Twenty-Second Edition
0-07-830973-5 Electricity Principles and Applications
1418065803 Delmar’s Standard Textbook of Electricity
978-0-9890350-5-7 The Antique Wireless Association Review Volume 31
1-887736-06-9 Crystal Set Projects
0-914126-02-4 Vintage Radio

See Also

For some of the techniques in this blog posts samples, I’ve written other posts in the past which cover their use.

To learn more about String Interpolation, see my post Fun With PowerShell Strings.

My post Fun With PowerShell Arrays will demonstrate how to work with arrays in PowerShell.

Conclusion

In this post we began with an overview of the ISBN, what it is and its history. We then dove into how to access the simple form of the OpenLibrary API to retrieve a books data based on the ISBN.

In my next post I will cover the use of OpenLibrary’s advanced API to get book data.

If you want to learn more about PowerShell, I have many courses on Pluralsight. Two courses in particular I would recommend are:

PowerShell 7 Quick Start for Developers on Linux, macOS, and Windows

Everyday PowerShell for Developers on Linux, macOS, and Windows