Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions docling-core/src/main/java/ai/docling/core/DoclingDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,15 @@ public static class EntityMention {
private String createdBy;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("label")
Expand Down Expand Up @@ -456,6 +461,9 @@ public static class SummaryMetaField {
private String createdBy;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
Expand Down Expand Up @@ -641,9 +649,15 @@ public static final class TitleItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -703,9 +717,15 @@ public static final class SectionHeaderItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -768,9 +788,15 @@ public static final class FieldHeadingItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -833,9 +859,15 @@ public static final class ListItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -902,9 +934,15 @@ public static final class CodeItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -987,9 +1025,15 @@ public static final class FormulaItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -1049,9 +1093,15 @@ public static final class TextItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -1111,9 +1161,15 @@ public static final class FieldValueItem implements BaseTextItem {
private List<ProvenanceItem> prov;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("formatting")
Expand Down Expand Up @@ -1283,6 +1339,9 @@ public static class CodeMetaField {
private String createdBy;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("language")
Expand All @@ -1309,6 +1368,9 @@ public static class DescriptionMetaField {
private String createdBy;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
Expand Down Expand Up @@ -1660,6 +1722,9 @@ public static class TableCell {
private Integer endColOffsetIdx;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("column_header")
Expand Down Expand Up @@ -1794,9 +1859,15 @@ public static class GraphCell {
private Integer cellId;

@JsonProperty("text")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String text;

@JsonProperty("orig")
@Nullable
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson2Deserializer.class)
@tools.jackson.databind.annotation.JsonDeserialize(using = DoclingDocument.EmptyStringToNullJackson3Deserializer.class)
private String orig;

@JsonProperty("prov")
Expand Down Expand Up @@ -2035,6 +2106,45 @@ public static class PageItem {
public static class Builder { }
}


/**
* Jackson 2 deserializer that maps an empty string value to {@code null}
* for {@code text} and {@code orig} fields.
*/
static final class EmptyStringToNullJackson2Deserializer
extends com.fasterxml.jackson.databind.deser.std.StdDeserializer<String> {

EmptyStringToNullJackson2Deserializer() {
super(String.class);
}

@Override
public String deserialize(com.fasterxml.jackson.core.JsonParser p,
com.fasterxml.jackson.databind.DeserializationContext ctx) throws java.io.IOException {
String value = p.getText();
return (value == null || value.isEmpty()) ? null : value;
}
}

/**
* Jackson 3 deserializer that maps an empty string value to {@code null}
* for {@code text} and {@code orig} fields.
*/
static final class EmptyStringToNullJackson3Deserializer
extends tools.jackson.databind.deser.std.StdDeserializer<String> {

EmptyStringToNullJackson3Deserializer() {
super(String.class);
}

@Override
public String deserialize(tools.jackson.core.JsonParser p,
tools.jackson.databind.DeserializationContext ctx) throws tools.jackson.core.JacksonException {
String value = p.getText();
return (value == null || value.isEmpty()) ? null : value;
}
}

@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
public static class Builder { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,4 +554,73 @@ void shouldRejectNonTrackKindOnTrackSource() {
assertThat(source.getKind()).isEqualTo("track");
}

@Test
void shouldKeepEmptyStringTextAndOrigViaBuilder() {
DoclingDocument.FormulaItem item =
DoclingDocument.FormulaItem.builder()
.selfRef("#/texts/0")
.contentLayer(DoclingDocument.ContentLayer.BODY)
.label(DocItemLabel.FORMULA)
.orig("")
.text("")
.build();

// Builder does not normalize empty strings; normalization happens via Jackson deserialization.
// Empty strings are still omitted from serialization by @JsonInclude(NON_EMPTY).
assertThat(item.getText()).isEmpty();
assertThat(item.getOrig()).isEmpty();
}

@Test
void shouldOmitEmptyStringTextAndOrigFromSerialization() throws Exception {
ObjectMapper mapper = new ObjectMapper();

DoclingDocument document =
DoclingDocument.builder()
.name("formula-document")
.text(
DoclingDocument.FormulaItem.builder()
.selfRef("#/texts/0")
.contentLayer(DoclingDocument.ContentLayer.BODY)
.label(DocItemLabel.FORMULA)
.orig("")
.text("")
.build())
.build();

String json = mapper.writeValueAsString(document);

assertThat(json).doesNotContain("\"text\"");
assertThat(json).doesNotContain("\"orig\"");
}

@Test
void shouldDeserializeEmptyStringTextAndOrigAsNull() throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json =
"""
{
"name": "formula-document",
"texts": [
{
"self_ref": "#/texts/0",
"content_layer": "body",
"label": "formula",
"orig": "",
"text": ""
}
]
}
""";

DoclingDocument document = mapper.readValue(json, DoclingDocument.class);

assertThat(document.getTexts()).hasSize(1);
assertThat(document.getTexts().get(0)).isInstanceOf(DoclingDocument.FormulaItem.class);
DoclingDocument.FormulaItem formulaItem =
(DoclingDocument.FormulaItem) document.getTexts().get(0);
assertThat(formulaItem.getText()).isNull();
assertThat(formulaItem.getOrig()).isNull();
}

}
Loading