Skip to content

Feature : Allow setting custom linefeed on DefaultXmlPrettyPrinter #568

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
374e125
feature allow custom line feed
JooHyukKim Feb 5, 2023
eb23bbd
Update XmlFactory.java
JooHyukKim Feb 5, 2023
c0b3e41
Correct method naming
JooHyukKim Feb 5, 2023
3b066cb
Set default and static default separately
JooHyukKim Feb 5, 2023
8595328
Make _lineFeed non static
JooHyukKim Feb 5, 2023
79564a4
Add dot to end documentation sentences.
JooHyukKim Feb 5, 2023
f515eba
Implment linefeed in XmlPrettyPrinter
JooHyukKim Feb 5, 2023
4cc4394
Implement linefeed only in DefaultXmlPrettyPrinter
JooHyukKim Feb 5, 2023
6974014
Update DefaultXmlPrettyPrinter.java
JooHyukKim Feb 5, 2023
b75bfdd
XML declaration not part of linefeed?
JooHyukKim Feb 5, 2023
18abcc2
Refactor : change names to newLine
JooHyukKim Feb 6, 2023
c764189
withCustomNewLine() method...
JooHyukKim Feb 10, 2023
af70ece
Apply review
JooHyukKim Feb 10, 2023
7deeb84
Fix : make FixedSpaceIndenter static
JooHyukKim Feb 10, 2023
8360276
Refactor : rename from linefeed to newline
JooHyukKim Feb 14, 2023
6187f3d
move SPACES char[] to outer DefaultXmlPrettyPrinter
JooHyukKim Feb 14, 2023
d0aaf64
Add default empty constructor
JooHyukKim Feb 14, 2023
29a2bbf
withCustomNewLine revert back to SYSTEM_DEFAULT_NEW_LINE if null is p…
JooHyukKim Feb 16, 2023
fc4fb17
replace \n with system linefeed to make platform independent test
JooHyukKim Feb 16, 2023
dd198f2
Update DefaultXmlPrettyPrinter copy constructor
JooHyukKim Feb 16, 2023
b43811c
Remove unrelated changes
JooHyukKim Feb 16, 2023
05b1f04
Remove unrelated formatting changes
JooHyukKim Feb 16, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ public interface Indenter
*/
protected boolean _spacesInObjectEntries = true;

/**
* By default, will try to set as System.getProperty("line.separator")
* Can later set custom lineFeed with withCustomlineFeed method
* @since 2.15
*/
private final static String DEFAULT_LINE_FEED;
static {
String lf = null;
try {
lf = System.getProperty("line.separator");
} catch (Throwable t) { } // access exception?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just catch Exception, not Throwable (ok unlike we'd get OOME or such but as a general good practice)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

late reply, but done 👍🏻


if (lf != null) {
DEFAULT_LINE_FEED = lf;
} else {
DEFAULT_LINE_FEED = "\n"; // incase system has changed property name? line.separator
}
}

protected static String _lineFeed = DEFAULT_LINE_FEED;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected String _lineFeed = DEFAULT_LINE_FEED; -- should not be static

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pjfanning I have a question.
Even if we make _lineFeed field non-static, we have static line-feeding Indenters like Lf2SpacesIndenter. Linefeed happens inside Lf2SpacesIndenter.writeIndentation().
So Indenters can't access non-static field of outer class. 🥲

How can we solve this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thoughts, line-feed outside of Lf2SpacesIndenter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lf2SpacesIndenter could have a constructor that takes an instance of DefaultXmlPrettyPrinter in its constructor. Or we could make Lf2SpacesIndenter non-static.


/*
/**********************************************************
/* State
Expand Down Expand Up @@ -126,6 +147,18 @@ public void indentObjectsWith(Indenter i)

public void spacesInObjectEntries(boolean b) { _spacesInObjectEntries = b; }

/**
* Sets custom linefeed
* @since 2.15
*/
public DefaultXmlPrettyPrinter withCustomLineFeed(String lineFeed) {
// 06-Feb-2023, joohyukkim: when JacksonException extends RuntimeExceptions, throw it?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove this comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

if (lineFeed != null) {
_lineFeed = lineFeed;
}
return this;
}

/*
/**********************************************************
/* Instantiatable impl
Expand Down Expand Up @@ -443,7 +476,7 @@ public void writeLeafXsiNilElement(XMLStreamWriter2 sw,
public void writePrologLinefeed(XMLStreamWriter2 sw) throws XMLStreamException
{
// 06-Dec-2015, tatu: Alternatively could try calling `writeSpace()`...
sw.writeRaw(Lf2SpacesIndenter.SYSTEM_LINE_SEPARATOR);
sw.writeRaw(_lineFeed);
}

/*
Expand Down Expand Up @@ -506,15 +539,6 @@ protected static class Lf2SpacesIndenter
{
private static final long serialVersionUID = 1L;

final static String SYSTEM_LINE_SEPARATOR;
static {
String lf = null;
try {
lf = System.getProperty("line.separator");
} catch (Throwable t) { } // access exception?
SYSTEM_LINE_SEPARATOR = (lf == null) ? "\n" : lf;
}

final static int SPACE_COUNT = 64;
final static char[] SPACES = new char[SPACE_COUNT];
static {
Expand All @@ -529,7 +553,7 @@ public Lf2SpacesIndenter() { }
@Override
public void writeIndentation(XMLStreamWriter2 sw, int level) throws XMLStreamException
{
sw.writeRaw(SYSTEM_LINE_SEPARATOR);
sw.writeRaw(_lineFeed);
level += level; // 2 spaces per level
while (level > SPACE_COUNT) { // should never happen but...
sw.writeRaw(SPACES, 0, SPACE_COUNT);
Expand All @@ -541,7 +565,7 @@ public void writeIndentation(XMLStreamWriter2 sw, int level) throws XMLStreamExc
@Override
public void writeIndentation(JsonGenerator jg, int level) throws IOException
{
jg.writeRaw(SYSTEM_LINE_SEPARATOR);
jg.writeRaw(_lineFeed);
level += level; // 2 spaces per level
while (level > SPACE_COUNT) { // should never happen but...
jg.writeRaw(SPACES, 0, SPACE_COUNT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter;

public class XmlPrettyPrinterTest extends XmlTestBase
{
Expand Down Expand Up @@ -188,4 +189,76 @@ public void testMultiLevel172() throws Exception
+"</Company>\n",
xml);
}

public void testLineFeed_withCustomLineFeed() throws Exception {
Company root = new Company();
root.employee.add(new Employee("abc"));

String xml = _xmlMapper.writer()
.with(new DefaultXmlPrettyPrinter().withCustomLineFeed("\n\rLF\n\r"))
.with(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.writeValueAsString(root);
// unify possible apostrophes to quotes
xml = a2q(xml);

// with indentation, should get linefeeds in prolog/epilog too
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\rLF\n\r"
+ "<Company>\n\rLF\n\r"
+ " <e>\n\rLF\n\r"
+ " <employee>\n\rLF\n\r"
+ " <id>abc</id>\n\rLF\n\r"
+ " <type>FULL_TIME</type>\n\rLF\n\r"
+ " </employee>\n\rLF\n\r"
+ " </e>\n\rLF\n\r"
+ "</Company>\n\rLF\n\r",
xml);
}

public void testLineFeed_systemDefault() throws Exception {
Company root = new Company();
root.employee.add(new Employee("abc"));

String xml = _xmlMapper.writer()
.with(new DefaultXmlPrettyPrinter())
.with(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.writeValueAsString(root);
// unify possible apostrophes to quotes
xml = a2q(xml);

// with indentation, should get linefeeds in prolog/epilog too
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to replace \n with system linefeed so test passes on Windows systems too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, done! 🙏🏼

And plus I checked other similar test cases' assertions and some hard coded \n as system linefeed.
@cowtowncoder if you agree, I will make a separate PR on "replacing all hard coded \n to the value read from System.getProperty("line.separator");"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sounds good wrt hard-coded linefeeds

+ "<Company>\n"
+ " <e>\n"
+ " <employee>\n"
+ " <id>abc</id>\n"
+ " <type>FULL_TIME</type>\n"
+ " </employee>\n"
+ " </e>\n"
+ "</Company>\n",
xml);
}

public void testLineFeed_UseSystemDefaultLineSeperatorOnNullCustomLineFeed() throws Exception {
Company root = new Company();
root.employee.add(new Employee("abc"));

String xml = _xmlMapper.writer()
.with(new DefaultXmlPrettyPrinter().withCustomLineFeed(null))
.with(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.writeValueAsString(root);
// unify possible apostrophes to quotes
xml = a2q(xml);

// with indentation, should get linefeeds in prolog/epilog too
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<Company>\n"
+ " <e>\n"
+ " <employee>\n"
+ " <id>abc</id>\n"
+ " <type>FULL_TIME</type>\n"
+ " </employee>\n"
+ " </e>\n"
+ "</Company>\n",
xml);
}
}