Net-Z3950-FOLIO/lib/Net/Z3950/FOLIO/Record.pm
package Net::Z3950::FOLIO::Record;
use strict;
use warnings;
use Scalar::Util qw(blessed reftype);
use XML::Simple;
use Net::Z3950::FOLIO::HoldingsRecords qw(makeHoldingsRecords);
use Net::Z3950::FOLIO::MARCHoldings qw(insertMARCHoldings);
use Net::Z3950::FOLIO::PostProcess::MARC qw(postProcessMARCRecord);
sub new {
my $class = shift();
my($rs, $offset, $json) = @_;
return bless {
rs => $rs, # back-reference
offset => $offset, # zero-based position within rs
json => $json,
holdingsStructure => undef,
processed => 0,
}, $class;
}
sub id {
my $this = shift();
my $id = $this->{json}->{id};
return $id;
}
sub rs {
my $this = shift();
return $this->{rs};
}
sub jsonStructure {
my $this = shift();
return $this->{json};
}
sub prettyJSON {
my $this = shift();
return _formatJSON($this->{json});
}
sub prettyXML {
my $this = shift();
return _formatXML($this->{json});
}
sub holdings {
my $this = shift();
my($marc) = @_;
if (!$this->{holdingsStructure}) {
$this->{holdingsStructure} = makeHoldingsRecords($this, $marc);
}
return $this->{holdingsStructure};
}
sub _marc2folioId {
my $marc = shift;
my @fields999 = $marc->field(999);
foreach my $field999 (@fields999) {
my $folioId = $field999->subfield('i');
return $folioId if $folioId;
}
return undef;
}
sub marcRecord {
my $this = shift();
my $rs = $this->{rs};
my $session = $rs->session();
my $marc = $this->{marc};
if (!defined $marc) {
# Fetch a chunk of records that contains the requested one.
my $chunkSize = $session->{cfg}->{chunkSize} || 10;
my $chunk = int($this->{offset} / $chunkSize);
my @marcRecords = $session->_getSRSRecords($rs, $chunk * $chunkSize, $chunkSize);
for (my $i = 0; $i < @marcRecords; $i++) {
my $marc = $marcRecords[$i];
my $id = _marc2folioId($marc);
Net::Z3950::FOLIO::_throw(1, "can't find FOLIO ID in MARC record", undef, 1) if !defined $id;
my $rec = $rs->recordById($id);
$rec->{marc} = $marcRecords[$i];
}
$marc = $this->{marc};
Net::Z3950::FOLIO::_throw(1, "missing MARC record", undef, 1) if !defined $marc;
}
if (!$this->{processed}) {
insertMARCHoldings($this, $marc, $session->{cfg}, $rs->barcode());
$marc = postProcessMARCRecord(($session->{cfg}->{postProcessing} || {})->{marc}, $marc);
$this->{marc} = $marc;
$this->{processed} = 1;
}
return $marc;
}
# ----------------------------------------------------------------------------
sub _formatJSON {
my($obj) = @_;
my $coder = Cpanel::JSON::XS->new->ascii->pretty->allow_blessed->space_before(0)->indent_length(2)->sort_by;
return $coder->encode($obj);
}
sub _formatXML {
my($json) = @_;
my $xml;
{
# Sanitize output to remove JSON::PP::Boolean values, which XMLout can't handle
_sanitizeTree($json);
# I have no idea why this generates an "uninitialized value" warning
local $SIG{__WARN__} = sub {};
$xml = XMLout($json, NoAttr => 1);
}
$xml =~ s/<@/<__/;
$xml =~ s/<\/@/<\/__/;
return $xml;
}
# This code modified from https://www.perlmonks.org/?node_id=773738
sub _sanitizeTree {
for my $node (@_) {
if (!defined($node)) {
next;
} elsif (ref($node) eq 'JSON::PP::Boolean') {
$node += 0;
} elsif (blessed($node)) {
use Data::Dumper;
die('_sanitizeTree: unexpected ', ref($node), ' object: ', Dumper($node));
} elsif (reftype($node)) {
if (ref($node) eq 'ARRAY') {
_sanitizeTree(@$node);
} elsif (ref($node) eq 'HASH') {
_sanitizeTree(values(%$node));
} else {
die('_sanitizeTree: unexpected reference type');
}
}
}
}
1;