fixed JSON geenration, added record label encoding, added lightweight record adapter, more tests
This commit is contained in:
parent
e4623a9e5c
commit
fa5f53fbd2
20 changed files with 348 additions and 193 deletions
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Jörg Prante
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package org.xbib.marc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LightweightMarcRecordAdapter extends MarcRecordAdapter {
|
||||||
|
|
||||||
|
public LightweightMarcRecordAdapter(MarcRecordListener marcRecordListener) {
|
||||||
|
super(marcRecordListener);
|
||||||
|
this.builder = Marc.builder().lightweightRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endRecord() {
|
||||||
|
super.endRecord();
|
||||||
|
builder = Marc.builder().lightweightRecord();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,7 @@
|
||||||
package org.xbib.marc;
|
package org.xbib.marc;
|
||||||
|
|
||||||
import static org.xbib.marc.MarcXchangeConstants.BIBLIOGRAPHIC_TYPE;
|
import static org.xbib.marc.MarcXchangeConstants.BIBLIOGRAPHIC_TYPE;
|
||||||
import static org.xbib.marc.MarcXchangeConstants.FORMAT_ATTRIBUTE;
|
|
||||||
import static org.xbib.marc.MarcXchangeConstants.MARCXCHANGE_FORMAT;
|
import static org.xbib.marc.MarcXchangeConstants.MARCXCHANGE_FORMAT;
|
||||||
import static org.xbib.marc.MarcXchangeConstants.TYPE_ATTRIBUTE;
|
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.xbib.marc.dialects.aleph.AlephSequentialInputStream;
|
import org.xbib.marc.dialects.aleph.AlephSequentialInputStream;
|
||||||
|
@ -495,12 +493,6 @@ public final class Marc {
|
||||||
}
|
}
|
||||||
if (builder.getProperties() != null) {
|
if (builder.getProperties() != null) {
|
||||||
for (Map.Entry<String, Object> entry : builder.getProperties().entrySet()) {
|
for (Map.Entry<String, Object> entry : builder.getProperties().entrySet()) {
|
||||||
if (FORMAT_ATTRIBUTE.equals(entry.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (TYPE_ATTRIBUTE.equals(entry.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sax.getXMLReader().setProperty(entry.getKey(), entry.getValue());
|
sax.getXMLReader().setProperty(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -969,17 +961,12 @@ public final class Marc {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used as there is no known input with collection events yet.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void beginCollection() {
|
public void beginCollection() {
|
||||||
if (listener != null) {
|
// not used
|
||||||
listener.beginCollection();
|
|
||||||
}
|
|
||||||
if (defaultContentHandler != null) {
|
|
||||||
defaultContentHandler.beginCollection();
|
|
||||||
}
|
|
||||||
if (marcRecordListener != null) {
|
|
||||||
marcRecordListener.beginCollection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1042,17 +1029,12 @@ public final class Marc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not used as there is no known input with collection events yet.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void endCollection() {
|
public void endCollection() {
|
||||||
if (listener != null) {
|
// not used
|
||||||
listener.endCollection();
|
|
||||||
}
|
|
||||||
if (defaultContentHandler != null) {
|
|
||||||
defaultContentHandler.endCollection();
|
|
||||||
}
|
|
||||||
if (marcRecordListener != null) {
|
|
||||||
marcRecordListener.endCollection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marc.Builder recordLabel(RecordLabel recordLabel) {
|
public Marc.Builder recordLabel(RecordLabel recordLabel) {
|
||||||
|
|
|
@ -563,6 +563,7 @@ public class MarcField implements Comparable<MarcField> {
|
||||||
* @param string the string to insert
|
* @param string the string to insert
|
||||||
* @return true if collection changed
|
* @return true if collection changed
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean add(String string) {
|
public boolean add(String string) {
|
||||||
ListIterator<String> it = listIterator();
|
ListIterator<String> it = listIterator();
|
||||||
boolean added = false;
|
boolean added = false;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.xbib.marc;
|
package org.xbib.marc;
|
||||||
|
|
||||||
import static org.xbib.marc.io.InformationSeparator.FS;
|
|
||||||
import static org.xbib.marc.io.InformationSeparator.GS;
|
import static org.xbib.marc.io.InformationSeparator.GS;
|
||||||
import static org.xbib.marc.io.InformationSeparator.RS;
|
import static org.xbib.marc.io.InformationSeparator.RS;
|
||||||
import static org.xbib.marc.io.InformationSeparator.US;
|
import static org.xbib.marc.io.InformationSeparator.US;
|
||||||
|
@ -141,11 +140,6 @@ public class MarcGenerator implements ChunkListener<byte[], BytesReference> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (separator) {
|
switch (separator) {
|
||||||
case FS: {
|
|
||||||
emitMarcField();
|
|
||||||
emitMarcRecord();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GS: {
|
case GS: {
|
||||||
emitMarcField();
|
emitMarcField();
|
||||||
emitMarcRecord();
|
emitMarcRecord();
|
||||||
|
@ -250,10 +244,6 @@ public class MarcGenerator implements ChunkListener<byte[], BytesReference> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void newRecord() throws IOException {
|
private void newRecord() throws IOException {
|
||||||
// checkguard
|
|
||||||
if (this.data == null || this.data.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// skip line-feed (OCLC PICA quirk)
|
// skip line-feed (OCLC PICA quirk)
|
||||||
if (this.data.charAt(0) == '\n') {
|
if (this.data.charAt(0) == '\n') {
|
||||||
this.data = data.substring(1);
|
this.data = data.substring(1);
|
||||||
|
|
|
@ -22,7 +22,7 @@ import static org.xbib.marc.json.MarcJsonWriter.TYPE_TAG;
|
||||||
|
|
||||||
import org.xbib.marc.label.RecordLabel;
|
import org.xbib.marc.label.RecordLabel;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -33,7 +33,7 @@ import java.util.stream.Collectors;
|
||||||
/**
|
/**
|
||||||
* A MARC record. This is an extended MARC record augmented with MarcXchange information.
|
* A MARC record. This is an extended MARC record augmented with MarcXchange information.
|
||||||
*/
|
*/
|
||||||
public class MarcRecord extends HashMap<String, Object> {
|
public class MarcRecord extends LinkedHashMap<String, Object> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 5305809148724342653L;
|
private static final long serialVersionUID = 5305809148724342653L;
|
||||||
|
|
||||||
|
@ -138,19 +138,15 @@ public class MarcRecord extends HashMap<String, Object> {
|
||||||
put(LEADER_TAG, recordLabel.toString());
|
put(LEADER_TAG, recordLabel.toString());
|
||||||
for (MarcField marcField : marcFields) {
|
for (MarcField marcField : marcFields) {
|
||||||
String tag = marcField.getTag();
|
String tag = marcField.getTag();
|
||||||
if (marcField.isControl()) {
|
|
||||||
put(tag, marcField.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!containsKey(tag)) {
|
if (!containsKey(tag)) {
|
||||||
put(tag, new HashMap<>());
|
put(tag, new LinkedHashMap<>());
|
||||||
}
|
}
|
||||||
String indicator = marcField.getIndicator();
|
String indicator = marcField.getIndicator();
|
||||||
if (indicator != null) {
|
if (indicator != null && !indicator.isEmpty()) {
|
||||||
indicator = indicator.replace(' ', '_');
|
indicator = indicator.replace(' ', '_');
|
||||||
Map<String, Object> indicators = (Map<String, Object>) get(tag);
|
Map<String, Object> indicators = (Map<String, Object>) get(tag);
|
||||||
if (!indicators.containsKey(indicator)) {
|
if (!indicators.containsKey(indicator)) {
|
||||||
indicators.put(indicator, new HashMap<>());
|
indicators.put(indicator, new LinkedHashMap<>());
|
||||||
}
|
}
|
||||||
Map<String, Object> subfields = (Map<String, Object>) indicators.get(indicator);
|
Map<String, Object> subfields = (Map<String, Object>) indicators.get(indicator);
|
||||||
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
||||||
|
@ -167,6 +163,8 @@ public class MarcRecord extends HashMap<String, Object> {
|
||||||
subfields.put(subfield.getId(), subfield.getValue());
|
subfields.put(subfield.getId(), subfield.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
put(tag, marcField.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 Jörg Prante
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
package org.xbib.marc;
|
package org.xbib.marc;
|
||||||
|
|
||||||
import org.xbib.marc.label.RecordLabel;
|
import org.xbib.marc.label.RecordLabel;
|
||||||
|
@ -7,13 +23,13 @@ import org.xbib.marc.label.RecordLabel;
|
||||||
*/
|
*/
|
||||||
public class MarcRecordAdapter implements MarcListener {
|
public class MarcRecordAdapter implements MarcListener {
|
||||||
|
|
||||||
private final MarcRecordListener marcRecordListener;
|
protected final MarcRecordListener marcRecordListener;
|
||||||
|
|
||||||
private Marc.Builder builder;
|
protected Marc.Builder builder;
|
||||||
|
|
||||||
public MarcRecordAdapter(MarcRecordListener marcRecordListener) {
|
public MarcRecordAdapter(MarcRecordListener marcRecordListener) {
|
||||||
this.marcRecordListener = marcRecordListener;
|
this.marcRecordListener = marcRecordListener;
|
||||||
this.builder = Marc.builder().lightweightRecord();
|
this.builder = Marc.builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,7 +56,7 @@ public class MarcRecordAdapter implements MarcListener {
|
||||||
@Override
|
@Override
|
||||||
public void endRecord() {
|
public void endRecord() {
|
||||||
marcRecordListener.record(builder.buildRecord());
|
marcRecordListener.record(builder.buildRecord());
|
||||||
builder = Marc.builder().lightweightRecord();
|
builder = Marc.builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.xbib.marc.io.BytesStreamOutput;
|
||||||
import org.xbib.marc.io.DefaultChunk;
|
import org.xbib.marc.io.DefaultChunk;
|
||||||
import org.xbib.marc.io.InformationSeparator;
|
import org.xbib.marc.io.InformationSeparator;
|
||||||
import org.xbib.marc.io.SeparatorOutputStream;
|
import org.xbib.marc.io.SeparatorOutputStream;
|
||||||
import org.xbib.marc.transformer.value.MarcValueTransformer;
|
import org.xbib.marc.transformer.value.MarcValueTransformers;
|
||||||
import org.xbib.marc.xml.MarcContentHandler;
|
import org.xbib.marc.xml.MarcContentHandler;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
@ -47,7 +47,7 @@ public class MarcWriter extends MarcContentHandler implements Flushable, Closeab
|
||||||
|
|
||||||
private final Charset charset;
|
private final Charset charset;
|
||||||
|
|
||||||
private MarcValueTransformer marcValueTransformer;
|
private MarcValueTransformers marcValueTransformers;
|
||||||
|
|
||||||
private boolean fatalErrors;
|
private boolean fatalErrors;
|
||||||
|
|
||||||
|
@ -76,20 +76,8 @@ public class MarcWriter extends MarcContentHandler implements Flushable, Closeab
|
||||||
this.bytesStreamOutput = new BytesStreamOutput();
|
this.bytesStreamOutput = new BytesStreamOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public MarcWriter setMarcValueTransformers(MarcValueTransformers marcValueTransformers) {
|
||||||
public MarcWriter setFormat(String format) {
|
this.marcValueTransformers = marcValueTransformers;
|
||||||
super.setFormat(format);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MarcWriter setType(String type) {
|
|
||||||
super.setType(type);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MarcWriter setMarcValueTransformer(MarcValueTransformer marcValueTransformer) {
|
|
||||||
this.marcValueTransformer = marcValueTransformer;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,30 +126,31 @@ public class MarcWriter extends MarcContentHandler implements Flushable, Closeab
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
MarcField marcField = marcValueTransformers != null ? marcValueTransformers.transformValue(field) : field;
|
||||||
bytesStreamOutput.reset();
|
bytesStreamOutput.reset();
|
||||||
// we clean up a bit. Write control field, and fields that are not empty.
|
// we clean up a bit. Write control field, and fields that are not empty.
|
||||||
// Do not care about the control field / data field order.
|
// Do not care about the control field / data field order.
|
||||||
if (field.isControl()) {
|
if (marcField.isControl()) {
|
||||||
String value = field.getValue();
|
String value = marcField.getValue();
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
bytesStreamOutput.write(field.getTag().getBytes(StandardCharsets.ISO_8859_1));
|
bytesStreamOutput.write(marcField.getTag().getBytes(StandardCharsets.ISO_8859_1));
|
||||||
bytesStreamOutput.write(transform(value).getBytes(charset));
|
bytesStreamOutput.write(value.getBytes(charset));
|
||||||
out.chunk(new DefaultChunk(InformationSeparator.RS, bytesStreamOutput.bytes()));
|
out.chunk(new DefaultChunk(InformationSeparator.RS, bytesStreamOutput.bytes()));
|
||||||
}
|
}
|
||||||
} else if (!field.isEmpty()) {
|
} else if (!marcField.isEmpty()) {
|
||||||
bytesStreamOutput.write(field.getTag().getBytes(StandardCharsets.ISO_8859_1));
|
bytesStreamOutput.write(marcField.getTag().getBytes(StandardCharsets.ISO_8859_1));
|
||||||
bytesStreamOutput.write(field.getIndicator().getBytes(StandardCharsets.ISO_8859_1));
|
bytesStreamOutput.write(marcField.getIndicator().getBytes(StandardCharsets.ISO_8859_1));
|
||||||
String value = field.getValue();
|
String value = marcField.getValue();
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
bytesStreamOutput.write(transform(value).getBytes(charset));
|
bytesStreamOutput.write(value.getBytes(charset));
|
||||||
}
|
}
|
||||||
out.chunk(new DefaultChunk(InformationSeparator.RS, bytesStreamOutput.bytes()));
|
out.chunk(new DefaultChunk(InformationSeparator.RS, bytesStreamOutput.bytes()));
|
||||||
for (MarcField.Subfield subfield : field.getSubfields()) {
|
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
||||||
value = subfield.getValue();
|
value = subfield.getValue();
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
bytesStreamOutput.reset();
|
bytesStreamOutput.reset();
|
||||||
bytesStreamOutput.write(subfield.getId().getBytes(StandardCharsets.ISO_8859_1));
|
bytesStreamOutput.write(subfield.getId().getBytes(StandardCharsets.ISO_8859_1));
|
||||||
bytesStreamOutput.write(transform(value).getBytes(charset));
|
bytesStreamOutput.write(value.getBytes(charset));
|
||||||
out.chunk(new DefaultChunk(InformationSeparator.US, bytesStreamOutput.bytes()));
|
out.chunk(new DefaultChunk(InformationSeparator.US, bytesStreamOutput.bytes()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,10 +194,6 @@ public class MarcWriter extends MarcContentHandler implements Flushable, Closeab
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String transform(String value) {
|
|
||||||
return marcValueTransformer != null ? marcValueTransformer.transform(value) : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleException(IOException e) {
|
private void handleException(IOException e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
if (fatalErrors) {
|
if (fatalErrors) {
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.xbib.marc.json;
|
package org.xbib.marc.json;
|
||||||
|
|
||||||
|
import org.xbib.marc.Marc;
|
||||||
import org.xbib.marc.MarcField;
|
import org.xbib.marc.MarcField;
|
||||||
import org.xbib.marc.MarcListener;
|
import org.xbib.marc.MarcListener;
|
||||||
import org.xbib.marc.MarcRecord;
|
import org.xbib.marc.MarcRecord;
|
||||||
|
import org.xbib.marc.label.RecordLabel;
|
||||||
import org.xbib.marc.xml.MarcContentHandler;
|
import org.xbib.marc.xml.MarcContentHandler;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
@ -30,6 +32,9 @@ import java.io.OutputStreamWriter;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@ -44,22 +49,18 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
|
|
||||||
public static final String TYPE_TAG = "_TYPE";
|
public static final String TYPE_TAG = "_TYPE";
|
||||||
|
|
||||||
private static final String EMPTY_STRING = "";
|
|
||||||
|
|
||||||
private static final String JSON_1 = "\":\"";
|
|
||||||
|
|
||||||
private final Lock lock = new ReentrantLock();
|
private final Lock lock = new ReentrantLock();
|
||||||
|
|
||||||
private final BufferedWriter writer;
|
private final BufferedWriter writer;
|
||||||
|
|
||||||
private final StringBuilder sb;
|
private final StringBuilder sb;
|
||||||
|
|
||||||
|
private Marc.Builder builder;
|
||||||
|
|
||||||
private boolean fatalErrors = false;
|
private boolean fatalErrors = false;
|
||||||
|
|
||||||
private boolean jsonlines;
|
private boolean jsonlines;
|
||||||
|
|
||||||
private int fieldCount;
|
|
||||||
|
|
||||||
private Exception exception;
|
private Exception exception;
|
||||||
|
|
||||||
public MarcJsonWriter(OutputStream out) throws IOException {
|
public MarcJsonWriter(OutputStream out) throws IOException {
|
||||||
|
@ -77,8 +78,8 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
public MarcJsonWriter(Writer writer, boolean jsonlines) throws IOException {
|
public MarcJsonWriter(Writer writer, boolean jsonlines) throws IOException {
|
||||||
this.writer = new BufferedWriter(writer);
|
this.writer = new BufferedWriter(writer);
|
||||||
this.sb = new StringBuilder();
|
this.sb = new StringBuilder();
|
||||||
this.fieldCount = 0;
|
|
||||||
this.jsonlines = jsonlines;
|
this.jsonlines = jsonlines;
|
||||||
|
this.builder = Marc.builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MarcJsonWriter setFatalErrors(boolean fatalErrors) {
|
public MarcJsonWriter setFatalErrors(boolean fatalErrors) {
|
||||||
|
@ -95,12 +96,14 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
@Override
|
@Override
|
||||||
public MarcJsonWriter setFormat(String format) {
|
public MarcJsonWriter setFormat(String format) {
|
||||||
super.setFormat(format);
|
super.setFormat(format);
|
||||||
|
builder.setFormat(format);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MarcJsonWriter setType(String type) {
|
public MarcJsonWriter setType(String type) {
|
||||||
super.setType(type);
|
super.setType(type);
|
||||||
|
builder.setType(type);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,35 +123,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
@Override
|
@Override
|
||||||
public void beginRecord(String format, String type) {
|
public void beginRecord(String format, String type) {
|
||||||
super.beginRecord(format, type);
|
super.beginRecord(format, type);
|
||||||
if (recordCounter.get() > 0) {
|
setFormat(format);
|
||||||
sb.append(jsonlines ? "\n" : ",");
|
setType(type);
|
||||||
}
|
|
||||||
sb.append("{");
|
|
||||||
String s = format != null ? format : this.format;
|
|
||||||
sb.append("\"").append(FORMAT_TAG).append("\":\"").append(escape(s)).append("\"");
|
|
||||||
fieldCount++;
|
|
||||||
s = type != null ? type : this.type;
|
|
||||||
if (fieldCount > 0) {
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
sb.append("\"").append(TYPE_TAG).append("\":\"").append(escape(s)).append("\"");
|
|
||||||
fieldCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leader(String label) {
|
public void leader(String label) {
|
||||||
super.leader(label);
|
super.leader(label);
|
||||||
if (fieldCount > 0) {
|
builder.recordLabel(RecordLabel.builder().from(label.toCharArray()).build());
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
sb.append("\"").append(LEADER_TAG).append("\":\"").append(label).append("\"");
|
|
||||||
fieldCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void field(MarcField field) {
|
public void field(MarcField field) {
|
||||||
super.field(field);
|
super.field(field);
|
||||||
fieldCount = toJson(field, fieldCount, sb);
|
builder.addField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,14 +162,14 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
@Override
|
@Override
|
||||||
public void endRecord() {
|
public void endRecord() {
|
||||||
super.endRecord();
|
super.endRecord();
|
||||||
try {
|
if (format != null) {
|
||||||
sb.append("}");
|
builder.setFormat(format);
|
||||||
writer.write(sb.toString());
|
|
||||||
sb.setLength(0);
|
|
||||||
recordCounter.incrementAndGet();
|
|
||||||
} catch (IOException e) {
|
|
||||||
handleException(e);
|
|
||||||
}
|
}
|
||||||
|
if (type != null) {
|
||||||
|
builder.setType(type);
|
||||||
|
}
|
||||||
|
record(builder.buildRecord());
|
||||||
|
builder = Marc.builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -211,30 +199,107 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
* Format MARC record as key-oriented JSON.
|
* Format MARC record as key-oriented JSON.
|
||||||
* @param sb a string builder to append JSON to
|
* @param sb a string builder to append JSON to
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private void toJson(MarcRecord marcRecord, StringBuilder sb) {
|
private void toJson(MarcRecord marcRecord, StringBuilder sb) {
|
||||||
|
if (marcRecord.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (recordCounter.get() > 0) {
|
if (recordCounter.get() > 0) {
|
||||||
sb.append(jsonlines ? "\n" : ",");
|
sb.append(jsonlines ? "\n" : ",");
|
||||||
}
|
}
|
||||||
sb.append("{");
|
sb.append("{");
|
||||||
int recordFieldCount = 0;
|
int c0 = 0;
|
||||||
if (format != null) {
|
for (Map.Entry<String, Object> tags : marcRecord.entrySet()) {
|
||||||
sb.append("\"_FORMAT\":\"").append(escape(format)).append("\"");
|
if (c0 > 0) {
|
||||||
recordFieldCount++;
|
|
||||||
}
|
|
||||||
if (type != null) {
|
|
||||||
if (recordFieldCount > 0) {
|
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
}
|
}
|
||||||
sb.append("\"_TYPE\":\"").append(escape(type)).append("\"");
|
String tag = tags.getKey();
|
||||||
recordFieldCount++;
|
sb.append("\"").append(tag).append("\":");
|
||||||
|
Object o = tags.getValue();
|
||||||
|
if (!(o instanceof List)) {
|
||||||
|
o = Collections.singletonList(o);
|
||||||
}
|
}
|
||||||
if (recordFieldCount > 0) {
|
List<?> list = (List<?>) o;
|
||||||
|
if (list.size() > 1) {
|
||||||
|
sb.append("[");
|
||||||
|
}
|
||||||
|
int c1 = 0;
|
||||||
|
for (Object value : list) {
|
||||||
|
if (c1 > 0) {
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
}
|
}
|
||||||
sb.append("\"_LEADER\":\"").append(marcRecord.getRecordLabel()).append("\"");
|
if (value instanceof Map) {
|
||||||
recordFieldCount++;
|
sb.append("{");
|
||||||
for (MarcField field : marcRecord.getFields()) {
|
int c2 = 0;
|
||||||
recordFieldCount = toJson(field, recordFieldCount, sb);
|
for (Map.Entry<String, Object> indicators : ((Map<String, Object>) value).entrySet()) {
|
||||||
|
if (c2 > 0) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
String indicator = indicators.getKey();
|
||||||
|
sb.append("\"").append(indicator).append("\":");
|
||||||
|
o = indicators.getValue();
|
||||||
|
if (!(o instanceof List)) {
|
||||||
|
o = Collections.singletonList(o);
|
||||||
|
}
|
||||||
|
List<?> list2 = (List<?>) o;
|
||||||
|
if (list2.size() > 1) {
|
||||||
|
sb.append("[");
|
||||||
|
}
|
||||||
|
int c3 = 0;
|
||||||
|
for (Object value2 : list2) {
|
||||||
|
if (c3 > 0) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
if (value2 instanceof Map) {
|
||||||
|
sb.append("{");
|
||||||
|
Map<String, Object> map = (Map<String, Object>) value2;
|
||||||
|
int c4 = 0;
|
||||||
|
for (Map.Entry<String, Object> subfield : map.entrySet()) {
|
||||||
|
if (c4 > 0) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("\"").append(subfield.getKey()).append("\":");
|
||||||
|
if (subfield.getValue() instanceof List) {
|
||||||
|
sb.append("[");
|
||||||
|
int c5 = 0;
|
||||||
|
for (String s : (List<String>)subfield.getValue()) {
|
||||||
|
if (c5 > 0) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append("\"").append(escape(s)).append("\"");
|
||||||
|
c5++;
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
} else {
|
||||||
|
sb.append("\"").append(escape(subfield.getValue().toString())).append("\"");
|
||||||
|
}
|
||||||
|
c4++;
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
} else {
|
||||||
|
sb.append("\"").append(escape(value2.toString())).append("\"");
|
||||||
|
}
|
||||||
|
c3++;
|
||||||
|
}
|
||||||
|
if (list2.size() > 1) {
|
||||||
|
sb.append("]");
|
||||||
|
}
|
||||||
|
c2++;
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
} else {
|
||||||
|
if (value == null) {
|
||||||
|
sb.append("null");
|
||||||
|
} else {
|
||||||
|
sb.append("\"").append(escape(value.toString())).append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c1++;
|
||||||
|
}
|
||||||
|
if (list.size() > 1) {
|
||||||
|
sb.append("]");
|
||||||
|
}
|
||||||
|
c0++;
|
||||||
}
|
}
|
||||||
sb.append('}');
|
sb.append('}');
|
||||||
if (jsonlines) {
|
if (jsonlines) {
|
||||||
|
@ -242,57 +307,6 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Print a key-oriented JSON represenation of this MARC field.
|
|
||||||
*
|
|
||||||
* @param fieldCount how many MARC field are writte before. Used for emitting a comma
|
|
||||||
* if necessary.
|
|
||||||
* @param sb the string builder to attach the JSON representation to.
|
|
||||||
*
|
|
||||||
* @return the new MARC field count. Empty MARC fields not not increase the field count.
|
|
||||||
*/
|
|
||||||
private int toJson(MarcField marcField, int fieldCount, StringBuilder sb) {
|
|
||||||
int count = fieldCount;
|
|
||||||
if (marcField.isControl()) {
|
|
||||||
if (count > 0) {
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
sb.append("\"").append(marcField.getTag()).append(JSON_1).append(escape(marcField.getValue())).append("\"");
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
} else if (!marcField.isEmpty()) {
|
|
||||||
if (count > 0) {
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
String tag = marcField.getTag();
|
|
||||||
String indicator = marcField.getIndicator();
|
|
||||||
if (indicator == null) {
|
|
||||||
indicator = EMPTY_STRING;
|
|
||||||
}
|
|
||||||
sb.append("\"").append(tag).append("\":{\"")
|
|
||||||
.append(indicator.replace(' ', '_')).append("\":");
|
|
||||||
if (marcField.getSubfields().size() == 1) {
|
|
||||||
MarcField.Subfield subfield = marcField.getSubfields().get(0);
|
|
||||||
sb.append("{\"").append(subfield.getId()).append(JSON_1).append(escape(subfield.getValue())).append("\"}");
|
|
||||||
} else {
|
|
||||||
sb.append("[");
|
|
||||||
StringBuilder subfieldBuilder = new StringBuilder();
|
|
||||||
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
|
||||||
if (subfieldBuilder.length() > 0) {
|
|
||||||
subfieldBuilder.append(",");
|
|
||||||
}
|
|
||||||
subfieldBuilder.append("{\"").append(subfield.getId()).append(JSON_1)
|
|
||||||
.append(escape(subfield.getValue())).append("\"}");
|
|
||||||
}
|
|
||||||
sb.append(subfieldBuilder);
|
|
||||||
sb.append("]");
|
|
||||||
}
|
|
||||||
sb.append("}");
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Exception getException() {
|
public Exception getException() {
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,10 @@ public class RecordLabel {
|
||||||
return builder.typeOfControl;
|
return builder.typeOfControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Encoding getEncoding() {
|
||||||
|
return builder.encoding;
|
||||||
|
}
|
||||||
|
|
||||||
public int getIndicatorLength() {
|
public int getIndicatorLength() {
|
||||||
return builder.indicatorLength;
|
return builder.indicatorLength;
|
||||||
}
|
}
|
||||||
|
@ -151,6 +155,8 @@ public class RecordLabel {
|
||||||
|
|
||||||
private TypeOfControl typeOfControl;
|
private TypeOfControl typeOfControl;
|
||||||
|
|
||||||
|
private Encoding encoding;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
cfix = empty;
|
cfix = empty;
|
||||||
repair();
|
repair();
|
||||||
|
@ -313,6 +319,17 @@ public class RecordLabel {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set encoding. See {@link Encoding}.
|
||||||
|
* @param encoding the encoding
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder setEncoding(Encoding encoding) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
cfix[9] = encoding.getChar();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicator length is a numeric digit giving the length of the indicators.
|
* Indicator length is a numeric digit giving the length of the indicators.
|
||||||
*
|
*
|
||||||
|
|
|
@ -76,7 +76,6 @@ public class ConcurrencyTest {
|
||||||
assertNull(writer.getException());
|
assertNull(writer.getException());
|
||||||
assertEquals(n * 292, writer.getRecordCounter());
|
assertEquals(n * 292, writer.getRecordCounter());
|
||||||
}
|
}
|
||||||
//assertEquals(8175000, file.length());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +115,6 @@ public class ConcurrencyTest {
|
||||||
assertNull(writer.getException());
|
assertNull(writer.getException());
|
||||||
assertEquals(n * 292, writer.getRecordCounter());
|
assertEquals(n * 292, writer.getRecordCounter());
|
||||||
}
|
}
|
||||||
assertEquals(175761 * n + 1, file.length());
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Write JSON lines format. This is shorter than array, because commas are not required.
|
* Write JSON lines format. This is shorter than array, because commas are not required.
|
||||||
|
@ -155,6 +153,5 @@ public class ConcurrencyTest {
|
||||||
assertNull(writer.getException());
|
assertNull(writer.getException());
|
||||||
assertEquals(n * 292, writer.getRecordCounter());
|
assertEquals(n * 292, writer.getRecordCounter());
|
||||||
}
|
}
|
||||||
assertEquals(176053 * n - 1, file.length());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,4 +198,28 @@ public class MarcRecordTest extends Assert {
|
||||||
assertThat(file, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
assertThat(file, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIRMARC8AsLightweightRecordAdapter() throws Exception {
|
||||||
|
String s = "IRMARC8.bin";
|
||||||
|
InputStream in = getClass().getResource(s).openStream();
|
||||||
|
File file = File.createTempFile(s + ".", ".xml");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
|
MarcValueTransformers marcValueTransformers = new MarcValueTransformers();
|
||||||
|
marcValueTransformers.setMarcValueTransformer(value -> Normalizer.normalize(value, Normalizer.Form.NFC));
|
||||||
|
try (MarcXchangeWriter writer = new MarcXchangeWriter(out)
|
||||||
|
.setMarcValueTransformers(marcValueTransformers)) {
|
||||||
|
writer.startDocument(); // just write XML processing instruction
|
||||||
|
Marc.builder()
|
||||||
|
.setInputStream(in)
|
||||||
|
.setCharset(Charset.forName("ANSEL"))
|
||||||
|
.setMarcListener(new LightweightMarcRecordAdapter(writer))
|
||||||
|
.build()
|
||||||
|
.writeCollection();
|
||||||
|
assertNull(writer.getException());
|
||||||
|
writer.endDocument();
|
||||||
|
}
|
||||||
|
assertThat(file, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.xbib.marc;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.xbib.marc.transformer.value.MarcValueTransformers;
|
||||||
import org.xbib.marc.xml.MarcXchangeWriter;
|
import org.xbib.marc.xml.MarcXchangeWriter;
|
||||||
import org.xmlunit.matchers.CompareMatcher;
|
import org.xmlunit.matchers.CompareMatcher;
|
||||||
|
|
||||||
|
@ -68,4 +69,46 @@ public class MarcWriterTest extends Assert {
|
||||||
assertThat(xmlFile, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
assertThat(xmlFile, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUtf8MarcWriterWithTransformer() throws Exception {
|
||||||
|
for (String s : new String[]{
|
||||||
|
"summerland.mrc",
|
||||||
|
"chabon.mrc",
|
||||||
|
"chabon-loc.mrc"
|
||||||
|
}) {
|
||||||
|
InputStream in = getClass().getResource(s).openStream();
|
||||||
|
File file = File.createTempFile(s, ".utf8");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
|
MarcValueTransformers marcValueTransformers = new MarcValueTransformers();
|
||||||
|
marcValueTransformers.setMarcValueTransformer("245$10$a", t -> t.replaceAll("Chabon","Chibon"));
|
||||||
|
try (MarcWriter writer = new MarcWriter(out, StandardCharsets.UTF_8)
|
||||||
|
.setMarcValueTransformers(marcValueTransformers)) {
|
||||||
|
Marc.builder()
|
||||||
|
.setInputStream(in)
|
||||||
|
.setCharset(Charset.forName("ANSEL"))
|
||||||
|
.setMarcListener(writer)
|
||||||
|
.build()
|
||||||
|
.writeCollection();
|
||||||
|
assertNull(writer.getException());
|
||||||
|
}
|
||||||
|
// re-read files with our Marc builder and write as MarcXchange
|
||||||
|
File xmlFile = File.createTempFile(s, ".utf8");
|
||||||
|
xmlFile.deleteOnExit();
|
||||||
|
out = new FileOutputStream(xmlFile);
|
||||||
|
marcValueTransformers = new MarcValueTransformers();
|
||||||
|
marcValueTransformers.setMarcValueTransformer("245$10$a", t -> t.replaceAll("Chibon","Chabon"));
|
||||||
|
try (MarcXchangeWriter writer = new MarcXchangeWriter(out)
|
||||||
|
.setMarcValueTransformers(marcValueTransformers)) {
|
||||||
|
Marc.builder()
|
||||||
|
.setInputStream(new FileInputStream(file))
|
||||||
|
.setMarcListener(writer)
|
||||||
|
.build()
|
||||||
|
.writeCollection();
|
||||||
|
}
|
||||||
|
// compare result
|
||||||
|
assertThat(xmlFile, CompareMatcher.isIdenticalTo(getClass().getResource(s + ".xml").openStream()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.junit.Test;
|
||||||
import org.xbib.marc.Marc;
|
import org.xbib.marc.Marc;
|
||||||
import org.xbib.marc.MarcRecordAdapter;
|
import org.xbib.marc.MarcRecordAdapter;
|
||||||
import org.xbib.marc.MarcXchangeConstants;
|
import org.xbib.marc.MarcXchangeConstants;
|
||||||
|
import org.xbib.marc.xml.MarcContentHandler;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -83,10 +84,10 @@ public class MarcJsonWriterTest {
|
||||||
file.deleteOnExit();
|
file.deleteOnExit();
|
||||||
FileOutputStream out = new FileOutputStream(file);
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
try (MarcJsonWriter writer = new MarcJsonWriter(out)
|
try (MarcJsonWriter writer = new MarcJsonWriter(out)
|
||||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
|
||||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
|
||||||
) {
|
) {
|
||||||
Marc.builder()
|
Marc.builder()
|
||||||
|
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||||
|
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||||
.setInputStream(in)
|
.setInputStream(in)
|
||||||
.setCharset(Charset.forName("ANSEL"))
|
.setCharset(Charset.forName("ANSEL"))
|
||||||
.setMarcRecordListener(writer)
|
.setMarcRecordListener(writer)
|
||||||
|
@ -115,11 +116,10 @@ public class MarcJsonWriterTest {
|
||||||
File file = File.createTempFile(s + ".", ".json");
|
File file = File.createTempFile(s + ".", ".json");
|
||||||
file.deleteOnExit();
|
file.deleteOnExit();
|
||||||
FileOutputStream out = new FileOutputStream(file);
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
try (MarcJsonWriter writer = new MarcJsonWriter(out)
|
try (MarcJsonWriter writer = new MarcJsonWriter(out)) {
|
||||||
|
Marc.builder()
|
||||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||||
) {
|
|
||||||
Marc.builder()
|
|
||||||
.setInputStream(in)
|
.setInputStream(in)
|
||||||
.setCharset(Charset.forName("ANSEL"))
|
.setCharset(Charset.forName("ANSEL"))
|
||||||
.setMarcListener(new MarcRecordAdapter(writer))
|
.setMarcListener(new MarcRecordAdapter(writer))
|
||||||
|
@ -130,4 +130,29 @@ public class MarcJsonWriterTest {
|
||||||
new FileInputStream(file));
|
new FileInputStream(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAlephPublishRecordAdapterJson() throws Exception {
|
||||||
|
String s = "HT016424175.xml";
|
||||||
|
InputStream in = getClass().getResource("/org/xbib/marc/dialects/mab/" + s).openStream();
|
||||||
|
File file = File.createTempFile(s + ".", ".json");
|
||||||
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
|
try (MarcJsonWriter writer = new MarcJsonWriter(out, true)
|
||||||
|
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||||
|
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||||
|
) {
|
||||||
|
MarcContentHandler contentHandler = new MarcContentHandler();
|
||||||
|
contentHandler.addNamespace("http://www.ddb.de/professionell/mabxml/mabxml-1.xsd");
|
||||||
|
contentHandler.setFormat("MARC21");
|
||||||
|
contentHandler.setType("Bibliographic");
|
||||||
|
contentHandler.setMarcListener(new MarcRecordAdapter(writer));
|
||||||
|
Marc.builder()
|
||||||
|
.setInputStream(in)
|
||||||
|
.setContentHandler(contentHandler)
|
||||||
|
.build()
|
||||||
|
.xmlReader().parse();
|
||||||
|
}
|
||||||
|
assertStream(s, getClass().getResource("/org/xbib/marc/json/" + s + ".json").openStream(),
|
||||||
|
new FileInputStream(file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,12 @@ public class RecordLabelTest {
|
||||||
assertEquals(RecordStatus.DELETED, recordLabel.getRecordStatus());
|
assertEquals(RecordStatus.DELETED, recordLabel.getRecordStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncoding() {
|
||||||
|
RecordLabel recordLabel = RecordLabel.builder().setEncoding(Encoding.MARC8).build();
|
||||||
|
assertEquals(Encoding.MARC8, recordLabel.getEncoding());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRecordLabelBuilder() {
|
public void testRecordLabelBuilder() {
|
||||||
RecordLabel recordLabel = RecordLabel.builder()
|
RecordLabel recordLabel = RecordLabel.builder()
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class ToolTest {
|
||||||
public void testToolSimple() throws Exception {
|
public void testToolSimple() throws Exception {
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"--in", "src/test/resources/org/xbib/marc/chabon.mrc",
|
"--in", "src/test/resources/org/xbib/marc/chabon.mrc",
|
||||||
|
"--charset", "ANSEL",
|
||||||
"--out", "build/chabon.mrc.xml"
|
"--out", "build/chabon.mrc.xml"
|
||||||
};
|
};
|
||||||
exit.expectSystemExitWithStatus(0);
|
exit.expectSystemExitWithStatus(0);
|
||||||
|
@ -42,6 +43,7 @@ public class ToolTest {
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"--in", "src/test/resources/org/xbib/marc/summerland.mrc",
|
"--in", "src/test/resources/org/xbib/marc/summerland.mrc",
|
||||||
"--out", "build/summerland.mrc.xml",
|
"--out", "build/summerland.mrc.xml",
|
||||||
|
"--charset", "ANSEL",
|
||||||
"--schema", "MARC21",
|
"--schema", "MARC21",
|
||||||
"--stylesheet", "http://www.loc.gov/standards/mods/v3/MARC21slim2MODS3.xsl",
|
"--stylesheet", "http://www.loc.gov/standards/mods/v3/MARC21slim2MODS3.xsl",
|
||||||
"--result", "build/summerland.mods"
|
"--result", "build/summerland.mods"
|
||||||
|
|
|
@ -67,4 +67,24 @@ public class MarcEventConsumerTest extends Assert {
|
||||||
assertNull(writer.getException());
|
assertNull(writer.getException());
|
||||||
assertThat(sw.toString(), CompareMatcher.isIdenticalTo(getClass().getResource(s + "-eventconsumer.xml").openStream()));
|
assertThat(sw.toString(), CompareMatcher.isIdenticalTo(getClass().getResource(s + "-eventconsumer.xml").openStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMarcXchangeWriterWithEventConsumer() throws Exception {
|
||||||
|
String s = "HT016424175.xml";
|
||||||
|
InputStream in = getClass().getResourceAsStream(s);
|
||||||
|
MarcXchangeEventConsumer consumer = new MarcXchangeEventConsumer();
|
||||||
|
consumer.addNamespace("http://www.ddb.de/professionell/mabxml/mabxml-1.xsd");
|
||||||
|
MarcXchangeWriter writer = new MarcXchangeWriter(consumer);
|
||||||
|
writer.setFormat("AlephXML").setType("Bibliographic");
|
||||||
|
writer.startDocument();
|
||||||
|
Marc.builder()
|
||||||
|
.setInputStream(in)
|
||||||
|
.setCharset(StandardCharsets.UTF_8)
|
||||||
|
.setFormat("AlephXML")
|
||||||
|
.setType("Bibliographic")
|
||||||
|
.build()
|
||||||
|
.writeCollection();
|
||||||
|
writer.endDocument();
|
||||||
|
assertNull(writer.getException());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"_FORMAT":"MARC21","_TYPE":"Bibliographic","_LEADER":"00000 M2.01200024 000h","LDR":"------M2.01200024------h","FMT":"MH","001":{"_1":{"a":"HT016424175"}},"002":{"a1":{"a":"20100705"}},"026":{"_1":{"a":"HBZHT016424175"}},"030":"a|1uc||||||1|","036":{"a1":{"a":"DE"}},"037":{"b1":{"a":"lat"}},"050":"a|||||||||||||","051":"am||||||","070":{"_1":{"a":"575"}},"080":{"_1":{"a":"60"}},"100":{"_1":{"p":"Hildegardis","c":"Bingensis","d":"1098-1179","9":"(DE-588)118550993"}},"101":{"_1":{"p":["Hildegard","Hildegard","Hildegard","Hildegardis","Hildegardis","Hildegarde","Ildegarda","Hildegardis","Hildegard","Hildegardis","Hildegardt","Hildegardis","Bingen, Hildegard <<von>>","Hildegard","Hildegardis","Ildegarda","Ildegarda","Hildegardis","Hildegard","Hildegarde","Ildegarda","Hildegardis","Hildegarda","Hildegarda","Hildegard","Childegard","Bingen, Childegard <<ot>>","Bingen, Hildegarde <<de>>","Hildegard","Hildegardis von Bingen","Hildegard","Hildegardis"],"d":["1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179","1098-1179"],"c":["Heilige, 1098-1179","Sankt","Sancta","Abbatissa","Sainte","de Bingen","de Monte Sancti Ruperti","die Heilige","de Bingen","of Bingen","Sancta Abatissa","Santa","Sant'","Abbess","Heilige","de Bingen","di Bingen","Abatissa","di Bingen","de Bingen","Bingeniläinen","ot Bingen","von Bingen","Sainte","de Alemannia"]}},"104":{"b1":{"p":"Escot, Pozzi","d":"1933-","b":"[Bearb.]","9":"(DE-588)128917687"}},"105":{"_1":{"p":"Pozzi Escot, Olga","d":"1933-"}},"304":{"b1":{"a":"Unde quocumque"}},"331":{"_1":{"a":"Unde quocumque"}},"334":{"_1":{"a":"Musikdruck"}},"359":{"_1":{"a":"Hildegard von Bingen"}},"425":{"_1":{"a":"c 1994"},"a1":{"a":"1994"}},"503":{"a1":{"a":"Transkription der mittelalterlichen Neumen in moderne Notation"}},"516":{"_1":{"a":"Melodien mit unterlegtem Text"}},"590":{"_1":{"a":"<<The>> Ursula Antiphons ... [Musikdruck]"}},"591":{"_1":{"a":"Hildegard von Bingen"}},"594":{"_1":{"a":"[Kassel]"}},"595":{"_1":{"a":"1994"}},"599":{"_1":{"a":"HT016424145"}},"SYS":"018117852"}
|
|
@ -1 +1 @@
|
||||||
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"01488cam 2200349 a 4500","001":"11939876","005":"20041229190604.0","008":"000313s2000 nyu 000 1 eng ","906":{"__":[{"a":"7"},{"b":"cbc"},{"c":"orignew"},{"d":"1"},{"e":"ocip"},{"f":"20"},{"g":"y-gencatlg"}]},"925":{"0_":[{"a":"acquire"},{"b":"2 shelf copies"},{"x":"policy default"}]},"955":{"__":[{"a":"to HLCD pc03 03-13-00; lh08 to subj. 03-14-00; lh06 03-22-00; lk02 03-22-00; to Dewey 03-22-00; aa05 03-23-00; ps13 2001-11-06 bk rec'd, to CIP ver."},{"f":"pv08 2001-11-07 CIP ver. to BCCD"}]},"010":{"__":{"a":" 00029063 "}},"020":{"__":{"a":"0679450041 (acid-free paper)"}},"040":{"__":[{"a":"DLC"},{"c":"DLC"},{"d":"DLC"}]},"043":{"__":{"a":"n-us-ny"}},"050":{"00":[{"a":"PS3553.H15"},{"b":"A82 2000"}]},"082":{"00":[{"a":"813/.54"},{"2":"21"}]},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"14":[{"a":"The amazing adventures of Kavalier and Clay :"},{"b":"a novel /"},{"c":"Michael Chabon."}]},"260":{"__":[{"a":"New York :"},{"b":"Random House,"},{"c":"c2000."}]},"300":{"__":[{"a":"639 p. ;"},{"c":"25 cm."}]},"650":{"_0":[{"a":"Comic books, strips, etc."},{"x":"Authorship"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Heroes in mass media"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Czech Americans"},{"v":"Fiction."}]},"651":{"_0":[{"a":"New York (N.Y.)"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Young men"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Cartoonists"},{"v":"Fiction."}]},"655":{"_7":[{"a":"Humorous stories."},{"2":"gsafd"}]},"655":{"_7":[{"a":"Bildungsromane."},{"2":"gsafd"}]},"856":{"42":[{"3":"Contributor biographical information"},{"u":"http://www.loc.gov/catdir/bios/random052/00029063.html"}]},"856":{"41":[{"3":"Sample text"},{"u":"http://www.loc.gov/catdir/samples/random044/00029063.html"}]},"856":{"42":[{"3":"Publisher description"},{"u":"http://www.loc.gov/catdir/description/random0411/00029063.html"}]}},{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"01185cam 2200301 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","906":{"__":[{"a":"7"},{"b":"cbc"},{"c":"orignew"},{"d":"1"},{"e":"ocip"},{"f":"20"},{"g":"y-gencatlg"}]},"925":{"0_":[{"a":"acquire"},{"b":"2 shelf copies"},{"x":"policy default"}]},"955":{"__":[{"a":"pc14 2002-08-05 to HLCD"},{"c":"lh08 2002-08-06 to subj.;"},{"d":"lb11 2002-09-05"},{"e":"lb05 2002-09-06 to cip"},{"a":"ps09 2003-03-04 1 copy rec'd., to CIP ver."},{"f":"pv01 2003-03-17 CIP ver to BCCD"},{"a":"ld11 2003-05-12 cp2 to BCCD"}]},"010":{"__":{"a":" 2002027497"}},"020":{"__":{"a":"0786808772"}},"020":{"__":{"a":"0786816155 (pbk.)"}},"040":{"__":[{"a":"DLC"},{"c":"DLC"},{"d":"DLC"}]},"042":{"__":{"a":"lcac"}},"050":{"00":[{"a":"PZ7.C3315"},{"b":"Su 2002"}]},"082":{"00":[{"a":"[Fic]"},{"2":"21"}]},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":[{"a":"Summerland /"},{"c":"Michael Chabon."}]},"250":{"__":{"a":"1st ed."}},"260":{"__":[{"a":"New York :"},{"b":"Miramax Books/Hyperion Books for Children,"},{"c":"c2002."}]},"300":{"__":[{"a":"500 p. ;"},{"c":"22 cm."}]},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":"Fantasy."}},"650":{"_1":[{"a":"Baseball"},{"v":"Fiction."}]},"650":{"_1":[{"a":"Magic"},{"v":"Fiction."}]},"952":{"__":{"a":"II lb11 09-05-02"}}}]
|
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"01488cam 2200349 a 4500","001":"11939876","005":"20041229190604.0","008":"000313s2000 nyu 000 1 eng ","906":{"__":{"a":"7","b":"cbc","c":"orignew","d":"1","e":"ocip","f":"20","g":"y-gencatlg"}},"925":{"0_":{"a":"acquire","b":"2 shelf copies","x":"policy default"}},"955":{"__":{"a":"to HLCD pc03 03-13-00; lh08 to subj. 03-14-00; lh06 03-22-00; lk02 03-22-00; to Dewey 03-22-00; aa05 03-23-00; ps13 2001-11-06 bk rec'd, to CIP ver.","f":"pv08 2001-11-07 CIP ver. to BCCD"}},"010":{"__":{"a":" 00029063 "}},"020":{"__":{"a":"0679450041 (acid-free paper)"}},"040":{"__":{"a":"DLC","c":"DLC","d":"DLC"}},"043":{"__":{"a":"n-us-ny"}},"050":{"00":{"a":"PS3553.H15","b":"A82 2000"}},"082":{"00":{"a":"813/.54","2":"21"}},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"14":{"a":"The amazing adventures of Kavalier and Clay :","b":"a novel /","c":"Michael Chabon."}},"260":{"__":{"a":"New York :","b":"Random House,","c":"c2000."}},"300":{"__":{"a":"639 p. ;","c":"25 cm."}},"650":{"_0":{"a":["Comic books, strips, etc.","Heroes in mass media","Czech Americans","Young men","Cartoonists"],"x":"Authorship","v":["Fiction.","Fiction.","Fiction.","Fiction.","Fiction."]}},"651":{"_0":{"a":"New York (N.Y.)","v":"Fiction."}},"655":{"_7":{"a":["Humorous stories.","Bildungsromane."],"2":["gsafd","gsafd"]}},"856":{"42":{"3":["Contributor biographical information","Publisher description"],"u":["http://www.loc.gov/catdir/bios/random052/00029063.html","http://www.loc.gov/catdir/description/random0411/00029063.html"]},"41":{"3":"Sample text","u":"http://www.loc.gov/catdir/samples/random044/00029063.html"}}},{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"01185cam 2200301 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","906":{"__":{"a":"7","b":"cbc","c":"orignew","d":"1","e":"ocip","f":"20","g":"y-gencatlg"}},"925":{"0_":{"a":"acquire","b":"2 shelf copies","x":"policy default"}},"955":{"__":{"a":["pc14 2002-08-05 to HLCD","ps09 2003-03-04 1 copy rec'd., to CIP ver.","ld11 2003-05-12 cp2 to BCCD"],"c":"lh08 2002-08-06 to subj.;","d":"lb11 2002-09-05","e":"lb05 2002-09-06 to cip","f":"pv01 2003-03-17 CIP ver to BCCD"}},"010":{"__":{"a":" 2002027497"}},"020":{"__":{"a":["0786808772","0786816155 (pbk.)"]}},"040":{"__":{"a":"DLC","c":"DLC","d":"DLC"}},"042":{"__":{"a":"lcac"}},"050":{"00":{"a":"PZ7.C3315","b":"Su 2002"}},"082":{"00":{"a":"[Fic]","2":"21"}},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":{"a":"Summerland /","c":"Michael Chabon."}},"250":{"__":{"a":"1st ed."}},"260":{"__":{"a":"New York :","b":"Miramax Books/Hyperion Books for Children,","c":"c2002."}},"300":{"__":{"a":"500 p. ;","c":"22 cm."}},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":["Fantasy.","Baseball","Magic"],"v":["Fiction.","Fiction."]}},"952":{"__":{"a":"II lb11 09-05-02"}}}]
|
|
@ -1 +1 @@
|
||||||
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00759cam a2200229 a 4500","001":"11939876","005":"20041229190604.0","008":"000313s2000 nyu 000 1 eng ","020":{"__":{"a":"0679450041 (acid-free paper)"}},"040":{"__":[{"a":"DLC"},{"c":"DLC"},{"d":"DLC"}]},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"14":[{"a":"The amazing adventures of Kavalier and Clay :"},{"b":"a novel /"},{"c":"Michael Chabon."}]},"260":{"__":[{"a":"New York :"},{"b":"Random House,"},{"c":"c2000."}]},"300":{"__":[{"a":"639 p. ;"},{"c":"25 cm."}]},"650":{"_0":[{"a":"Comic books, strips, etc."},{"x":"Authorship"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Heroes in mass media"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Czech Americans"},{"v":"Fiction."}]},"651":{"_0":[{"a":"New York (N.Y.)"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Young men"},{"v":"Fiction."}]},"650":{"_0":[{"a":"Cartoonists"},{"v":"Fiction."}]},"655":{"_7":[{"a":"Humorous stories."},{"2":"gsafd"}]},"655":{"_7":[{"a":"Bildungsromane."},{"2":"gsafd"}]}},{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00714cam a2200205 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","020":{"__":{"a":"0786808772"}},"020":{"__":{"a":"0786816155 (pbk.)"}},"040":{"__":[{"a":"DLC"},{"c":"DLC"},{"d":"DLC"}]},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":[{"a":"Summerland /"},{"c":"Michael Chabon."}]},"250":{"__":{"a":"1st ed."}},"260":{"__":[{"a":"New York :"},{"b":"Miramax Books/Hyperion Books for Children,"},{"c":"c2002."}]},"300":{"__":[{"a":"500 p. ;"},{"c":"22 cm."}]},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":"Fantasy."}},"650":{"_1":[{"a":"Baseball"},{"v":"Fiction."}]},"650":{"_1":[{"a":"Magic"},{"v":"Fiction."}]}}]
|
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00759cam a2200229 a 4500","001":"11939876","005":"20041229190604.0","008":"000313s2000 nyu 000 1 eng ","020":{"__":{"a":"0679450041 (acid-free paper)"}},"040":{"__":{"a":"DLC","c":"DLC","d":"DLC"}},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"14":{"a":"The amazing adventures of Kavalier and Clay :","b":"a novel /","c":"Michael Chabon."}},"260":{"__":{"a":"New York :","b":"Random House,","c":"c2000."}},"300":{"__":{"a":"639 p. ;","c":"25 cm."}},"650":{"_0":{"a":["Comic books, strips, etc.","Heroes in mass media","Czech Americans","Young men","Cartoonists"],"x":"Authorship","v":["Fiction.","Fiction.","Fiction.","Fiction.","Fiction."]}},"651":{"_0":{"a":"New York (N.Y.)","v":"Fiction."}},"655":{"_7":{"a":["Humorous stories.","Bildungsromane."],"2":["gsafd","gsafd"]}}},{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00714cam a2200205 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","020":{"__":{"a":["0786808772","0786816155 (pbk.)"]}},"040":{"__":{"a":"DLC","c":"DLC","d":"DLC"}},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":{"a":"Summerland /","c":"Michael Chabon."}},"250":{"__":{"a":"1st ed."}},"260":{"__":{"a":"New York :","b":"Miramax Books/Hyperion Books for Children,","c":"c2002."}},"300":{"__":{"a":"500 p. ;","c":"22 cm."}},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":["Fantasy.","Baseball","Magic"],"v":["Fiction.","Fiction."]}}}]
|
|
@ -1 +1 @@
|
||||||
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00714cam a2200205 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","020":{"__":{"a":"0786808772"}},"020":{"__":{"a":"0786816155 (pbk.)"}},"040":{"__":[{"a":"DLC"},{"c":"DLC"},{"d":"DLC"}]},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":[{"a":"Summerland /"},{"c":"Michael Chabon."}]},"250":{"__":{"a":"1st ed."}},"260":{"__":[{"a":"New York :"},{"b":"Miramax Books/Hyperion Books for Children,"},{"c":"c2002."}]},"300":{"__":[{"a":"500 p. ;"},{"c":"22 cm."}]},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":"Fantasy."}},"650":{"_1":[{"a":"Baseball"},{"v":"Fiction."}]},"650":{"_1":[{"a":"Magic"},{"v":"Fiction."}]}}]
|
[{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"00714cam a2200205 a 4500","001":"12883376","005":"20030616111422.0","008":"020805s2002 nyu j 000 1 eng ","020":{"__":{"a":["0786808772","0786816155 (pbk.)"]}},"040":{"__":{"a":"DLC","c":"DLC","d":"DLC"}},"100":{"1_":{"a":"Chabon, Michael."}},"245":{"10":{"a":"Summerland /","c":"Michael Chabon."}},"250":{"__":{"a":"1st ed."}},"260":{"__":{"a":"New York :","b":"Miramax Books/Hyperion Books for Children,","c":"c2002."}},"300":{"__":{"a":"500 p. ;","c":"22 cm."}},"520":{"__":{"a":"Ethan Feld, the worst baseball player in the history of the game, finds himself recruited by a 100-year-old scout to help a band of fairies triumph over an ancient enemy."}},"650":{"_1":{"a":["Fantasy.","Baseball","Magic"],"v":["Fiction.","Fiction."]}}}]
|
Loading…
Reference in a new issue