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

Conversation

JooHyukKim
Copy link
Member

BEFORE

  • Linefeed is set when static { } block from Lf2SpacesIndenter is run.
  • both DefaultXmlPrettyPrinterand Indenters both use static Lf2SpacesIndenter.SYSTEM_LINE_SEPARATOR

AFTER

  • _lineFeed is set when static { } block from DefaultXmlPrettyPrinter is run.
  • Indenters ONLY customize indentations and linefeed value is given by DefaultXmlPrettyPrinter

}
}

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.

@pjfanning
Copy link
Member

I have to ask why you are not ensuring the code compiles?

The @Override should not have been added to the new lineFeed() method. In fact, it should not even be necessary. It is likely that Lf2SpacesIndenter can access the defaultXmlPrettyPrinter._lineFeed.

@JooHyukKim
Copy link
Member Author

I have to ask why you are not ensuring the code compiles?

  • My mistake, won't happen again 👍🏻👍🏻

The @Override should not have been added to the new lineFeed() method. In fact, it should not even be necessary. It is likely that Lf2SpacesIndenter can access the defaultXmlPrettyPrinter._lineFeed.

  • I making Lf2SpacesIndenter non-static at the moment. Sorry for the confusion

xml = a2q(xml);

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

@JooHyukKim JooHyukKim Feb 5, 2023

Choose a reason for hiding this comment

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

I am not sure if this line should end with customLineFeed instead of `\n?

Copy link
Member

Choose a reason for hiding this comment

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

see the answer to your follow up question

+ " <type>FULL_TIME</type>\n\rLF\n\r"
+ " </employee>\n\rLF\n\r"
+ " </e>\n\rLF\n\r"
+ "</Company>\n",
Copy link
Member Author

@JooHyukKim JooHyukKim Feb 5, 2023

Choose a reason for hiding this comment

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

I am not sure if this line should end with customLineFeed instead of `\n?

Copy link
Member

Choose a reason for hiding this comment

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

yes - all the line feeds should be the same

actually, could we rename the method as withCustomNewLine? - I don't think 'line feed' is a good name for this property at all - 'new line' is better

Copy link
Member Author

@JooHyukKim JooHyukKim Feb 5, 2023

Choose a reason for hiding this comment

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

yes - all the line feeds should be the same

noted will make sure tests are applied there?

actually, could we rename the method as withCustomNewLine? - I don't think 'line feed' is a good name for this property at all - 'new line' is better

Agreed and I will change names as below!🙏🏼

  • _lineFeed -> _newLine
  • DEFAULT_LINE_FEED -> SYSTEM_DEFAULT_NEW_LINE
  • withCustomLineFeed -> withCustomNewLine

Copy link
Member Author

@JooHyukKim JooHyukKim Feb 6, 2023

Choose a reason for hiding this comment

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

@pjfanning So here is an update.


actually, could we rename the method as withCustomNewLine? - I don't think 'line feed' is a good name for this property at all - 'new line' is better

Done.
All names are changed from 'line feed' to 'new line'


yes - all the line feeds should be the same

May I get your opinion here? It seems like writing custom 'new lines' before and after root element might be out of this PR's scope. The 'new lines' before and after the root element are written by method writePrologLinefeed() of another instance of DefaultXmlPrettyPrinter.

This instance invoking writePrologLinefeed() instance seems to be initialized and used inside ToXmlGenerator which we cannot hook into from neither XmlMapper().setDefaultPrettyPrinter() nor ObjectWriter.with(PrettyPrinter).

For references,

Copy link
Member

Choose a reason for hiding this comment

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

It makes little sense to me not to use the same new lines throughout the XML doc. I'm now -1 on this PR.

Copy link
Member

Choose a reason for hiding this comment

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

@JooHyukKim Ok that sounds like a problem indeed! I wonder if it's the case of Indenter instance not being created yet (with createInstance()) or what is happening.

Copy link
Member Author

@JooHyukKim JooHyukKim Feb 14, 2023

Choose a reason for hiding this comment

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

Not the indenter, actually. There is a separate instance of DefaultXmlPrettyPrinter initialized together with ToXmlGenerator that invokes _xmlPrettyPrinter.writePrologLinefeed to write prolog and epilog.

You can refer to below to code lines.

My first (probable) observation is that writing prolog and epilog with instance of DefaultXmlPrettyPrinter I mentioned above seems NOT to be overridden by ObjectMapper.setDefaultPrettyPrinter(PrettyPrinter) nor ObjectWriter.with(PrettyPrinter).

Second observation is ToXmlGenerator writes prolog during the process of initialization within ToXmlGenerator.initGenerator method.

It will be great, @cowtowncoder if you can help out a little here? To override DefaultXmlPrettyPrinter of ToXmlGenerator._xmlPrettyPrinter before it writes prolog and epilog?

Copy link
Member

Choose a reason for hiding this comment

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

Right, pretty printer instance. I will try to see if I can figure it out -- the problem is that XML module extends basic PrettyPrinter API to its needs but databind still tries to orchestrate handling. So I will try to see if I can figure out how to avoid there being different instance being used.

Copy link
Member

Choose a reason for hiding this comment

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

@JooHyukKim Ah. Part of the problem is that you haven't updated copy constructor:

protected DefaultXmlPrettyPrinter(DefaultXmlPrettyPrinter base) {  }

to copy _newLine property -- so when method

    @Override
    public DefaultXmlPrettyPrinter createInstance() {

is called the new instance "forgets" _newLine. So you need to add

    _newLine = base._newLine;

in that constructor.

Copy link
Member Author

Choose a reason for hiding this comment

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

is called the new instance "forgets" _newLine. So you need to add

Woooooow thank you for helping out 🥹 @cowtowncoder
How did I miss that!
Done 🙏🏼

@JooHyukKim JooHyukKim requested a review from pjfanning February 5, 2023 22:56
* @since 2.15
*/
public DefaultXmlPrettyPrinter withCustomNewLine(String newLine) {
// 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 :)

+ " <type>FULL_TIME</type>\n\rLF\n\r"
+ " </employee>\n\rLF\n\r"
+ " </e>\n\rLF\n\r"
+ "</Company>\n",
Copy link
Member

Choose a reason for hiding this comment

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

It makes little sense to me not to use the same new lines throughout the XML doc. I'm now -1 on this PR.

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 👍🏻

@@ -473,7 +501,7 @@ public NopIndenter() { }
* single space for indentation. It is used as the default
* indenter for array values.
*/
protected static class FixedSpaceIndenter
protected class FixedSpaceIndenter
Copy link
Member

Choose a reason for hiding this comment

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

Same here, keep static (no need to refer to enclosing class)

@JooHyukKim
Copy link
Member Author

Sorry it took me long to react! 🙏🏼
I just moved house 🥲🥲🥲 and it's taking so much time to pack and unpack stuff

I just got internet back, I will try to apply your feed back asap.

@JooHyukKim
Copy link
Member Author

JooHyukKim commented Feb 14, 2023

All reviews made up to this point are applied to the changes.

The only thing left is being discussed in the thread of comments above. Simply put, about how to(or whether) to apply custom new line while writing XML declaration with ToXmlGenerator.Feature.WRITE_XML_DECLARATION or not.

xml = a2q(xml);

// with indentation, should get newLines 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

*/
public DefaultXmlPrettyPrinter withCustomNewLine(String newLine) {
if (newLine != null) {
_newLine = newLine;
Copy link
Member

Choose a reason for hiding this comment

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

Why not set _newLine to SYSTEM_DEFAULT_NEW_LINE if null is passed? This would allow reverting back to system newline.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, I don't see any problem doing so.
done!

@cowtowncoder cowtowncoder merged commit 74ac5ac into FasterXML:2.15 Feb 17, 2023
@cowtowncoder
Copy link
Member

Thank you again @JooHyukKim ! Merged for inclusion in 2.15.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants