///|
fn field_object(tag : String, value : String) -> Json {
Json::object(
Map([("tag", Json::string(tag)), ("value", Json::string(value))]),
)
}
///|
fn subfield_object(subfield : @record.Subfield) -> Json {
Json::object(
Map([
("code", Json::string(subfield.code.to_string())),
("value", Json::string(subfield.value)),
]),
)
}
///|
fn field_to_json(field : @record.MarcField) -> Json {
match field {
@record.MarcField::ControlField(tag~, value~) => field_object(tag, value)
@record.MarcField::DataField(tag~, indicator1~, indicator2~, subfields~) => {
let encoded_subfields : Array[Json] = []
for subfield in subfields {
encoded_subfields.push(subfield_object(subfield))
}
Json::object(
Map([
("tag", Json::string(tag)),
(
"indicators",
Json::array([
Json::string(indicator1.to_string()),
Json::string(indicator2.to_string()),
]),
),
("subfields", Json::array(encoded_subfields)),
]),
)
}
}
}
///|
/// Convert one structured MARC 21 record to the MoonMARC JSON shape.
pub fn record_to_json(record : @record.MarcRecord) -> Json {
let fields : Array[Json] = []
for field in record.fields() {
fields.push(field_to_json(field))
}
Json::object(
Map([
("leader", Json::string(record.leader().raw())),
("fields", Json::array(fields)),
]),
)
}
///|
/// Convert multiple structured MARC records to a JSON array.
pub fn records_to_json(records : Array[@record.MarcRecord]) -> Json {
let values : Array[Json] = []
for record in records {
values.push(record_to_json(record))
}
Json::array(values)
}
///|
/// Render one record as JSON text. The optional indent is passed to the core JSON stringifier.
pub fn record_to_json_text(
record : @record.MarcRecord,
indent? : Int,
) -> String {
match indent {
None => record_to_json(record).stringify()
Some(width) => record_to_json(record).stringify(indent=width)
}
}
///|
/// Render multiple records as a JSON array text.
pub fn records_to_json_text(
records : Array[@record.MarcRecord],
indent? : Int,
) -> String {
match indent {
None => records_to_json(records).stringify()
Some(width) => records_to_json(records).stringify(indent=width)
}
}
///|
fn expect_string(
object : Map[String, Json],
key : String,
) -> Result[String, JsonConversionError] {
match object.get(key) {
None => Err(MissingField(key))
Some(Json::String(value)) => Ok(value)
Some(_) => Err(WrongType(key))
}
}
///|
fn expect_array(
object : Map[String, Json],
key : String,
) -> Result[Array[Json], JsonConversionError] {
match object.get(key) {
None => Err(MissingField(key))
Some(Json::Array(values)) => Ok(values)
Some(_) => Err(WrongType(key))
}
}
///|
fn is_control_tag(tag : String) -> Bool {
tag >= "001" && tag <= "009"
}
///|
fn valid_tag(tag : String) -> Bool {
if tag.length() != 3 {
return false
}
for character in tag {
if character.to_int() > 0x7F {
return false
}
}
true
}
///|
fn char_from_json(
value : Json,
field : String,
) -> Result[Char, JsonConversionError] {
match value {
Json::String(text) => {
if text.length() != 1 {
return Err(InvalidValue(field))
}
let character = text.get_char(0).unwrap()
if character.to_int() > 0x7F {
return Err(InvalidValue(field))
}
Ok(character)
}
_ => Err(WrongType(field))
}
}
///|
fn parse_subfield(
value : Json,
) -> Result[@record.Subfield, JsonConversionError] {
let object = match value {
Json::Object(object) => object
_ => return Err(WrongType("subfields"))
}
let code_text = match expect_string(object, "code") {
Err(error) => return Err(error)
Ok(value) => value
}
if code_text.length() != 1 {
return Err(InvalidValue("subfield.code"))
}
let code = code_text.get_char(0).unwrap()
if code.to_int() > 0x7F {
return Err(InvalidValue("subfield.code"))
}
let text = match expect_string(object, "value") {
Err(error) => return Err(error)
Ok(value) => value
}
Ok({ code, value: text })
}
///|
fn parse_field(value : Json) -> Result[@record.MarcField, JsonConversionError] {
let object = match value {
Json::Object(object) => object
_ => return Err(WrongType("fields"))
}
let tag = match expect_string(object, "tag") {
Err(error) => return Err(error)
Ok(value) => value
}
if !valid_tag(tag) {
return Err(InvalidTag(tag))
}
match object.get("value") {
Some(Json::String(text)) => {
if !is_control_tag(tag) {
return Err(InvalidValue("value"))
}
Ok(@record.MarcField::ControlField(tag~, value=text))
}
Some(_) => Err(WrongType("value"))
None => {
if is_control_tag(tag) {
return Err(InvalidValue("control field"))
}
let indicators = match expect_array(object, "indicators") {
Err(error) => return Err(error)
Ok(value) => value
}
if indicators.length() != 2 {
return Err(InvalidValue("indicators"))
}
let indicator1 = match char_from_json(indicators[0], "indicators[0]") {
Err(error) => return Err(error)
Ok(value) => value
}
let indicator2 = match char_from_json(indicators[1], "indicators[1]") {
Err(error) => return Err(error)
Ok(value) => value
}
let subfield_values = match expect_array(object, "subfields") {
Err(error) => return Err(error)
Ok(value) => value
}
let subfields : Array[@record.Subfield] = []
for subfield_value in subfield_values {
subfields.push(
match parse_subfield(subfield_value) {
Err(error) => return Err(error)
Ok(value) => value
},
)
}
Ok(
@record.MarcField::DataField(tag~, indicator1~, indicator2~, subfields~),
)
}
}
}
///|
/// Convert one JSON value in the MoonMARC interchange shape to a record.
pub fn record_from_json(
value : Json,
) -> Result[@record.MarcRecord, JsonConversionError] {
let object = match value {
Json::Object(object) => object
_ => return Err(WrongType("record"))
}
let leader_text = match expect_string(object, "leader") {
Err(error) => return Err(error)
Ok(value) => value
}
let leader = match @leader.parse(@binary.ascii(leader_text)) {
Err(error) => return Err(InvalidLeader(error.to_string()))
Ok(value) => value
}
let field_values = match expect_array(object, "fields") {
Err(error) => return Err(error)
Ok(value) => value
}
let fields : Array[@record.MarcField] = []
for field_value in field_values {
fields.push(
match parse_field(field_value) {
Err(error) => return Err(error)
Ok(value) => value
},
)
}
Ok({ leader, fields })
}
///|
/// Convert a JSON array to structured MARC 21 records.
pub fn records_from_json(
value : Json,
) -> Result[Array[@record.MarcRecord], JsonConversionError] {
let values = match value {
Json::Array(values) => values
_ => return Err(WrongType("records"))
}
let records : Array[@record.MarcRecord] = []
for item in values {
records.push(
match record_from_json(item) {
Err(error) => return Err(error)
Ok(record) => record
},
)
}
Ok(records)
}
///|
/// Parse JSON text and convert it to one structured MARC 21 record.
pub fn record_from_json_text(
text : String,
) -> Result[@record.MarcRecord, JsonConversionError] {
let value = @json.parse(text) catch {
error => return Err(InvalidJson(error.to_string()))
}
record_from_json(value)
}
///|
/// Parse JSON text containing an array of records.
pub fn records_from_json_text(
text : String,
) -> Result[Array[@record.MarcRecord], JsonConversionError] {
let value = @json.parse(text) catch {
error => return Err(InvalidJson(error.to_string()))
}
records_from_json(value)
}