<div dir="auto">Here’s a trial balloon:</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto"><div style="font-size:inherit" dir="auto">import org.xml.sax.SAXException;<br style="font-size:inherit">import org.xml.sax.ext.DefaultHandler2;<br style="font-size:inherit">import java.io.IOException;<br style="font-size:inherit">import java.io.Writer;<br style="font-size:inherit">import java.util.regex.Pattern;<br style="font-size:inherit"><br style="font-size:inherit">public class CommentNormalizingHandler extends DefaultHandler2 {<br style="font-size:inherit"> private Writer writer;<br style="font-size:inherit"> private Pattern attributeWithNewlinePattern;<br style="font-size:inherit"> <br style="font-size:inherit"> public CommentNormalizingHandler(Writer writer) {<br style="font-size:inherit"> this.writer = writer;<br style="font-size:inherit"> // Matches newlines inside quoted attribute values<br style="font-size:inherit"> this.attributeWithNewlinePattern = Pattern.compile("['\"][^'\"]*\\n[^'\"]*['\"]");<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void comment(char[] ch, int start, int length) throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> String commentText = new String(ch, start, length);<br style="font-size:inherit"> <br style="font-size:inherit"> // Check if comment contains newlines inside attribute values<br style="font-size:inherit"> if (attributeWithNewlinePattern.matcher(commentText).find()) {<br style="font-size:inherit"> // Normalize whitespace but keep as single comment<br style="font-size:inherit"> String normalized = commentText.trim().replaceAll("\\s+", " ");<br style="font-size:inherit"> writer.write("<!--");<br style="font-size:inherit"> writer.write(normalized);<br style="font-size:inherit"> writer.write("-->");<br style="font-size:inherit"> } else {<br style="font-size:inherit"> // Split into multiple single-line comments<br style="font-size:inherit"> String[] lines = commentText.split("\\n");<br style="font-size:inherit"> for (String line : lines) {<br style="font-size:inherit"> String trimmed = line.trim();<br style="font-size:inherit"> if (!trimmed.isEmpty()) {<br style="font-size:inherit"> writer.write("<!--");<br style="font-size:inherit"> writer.write(trimmed);<br style="font-size:inherit"> writer.write("-->");<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing comment", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void startElement(String uri, String localName, String qName, <br style="font-size:inherit"> org.xml.sax.Attributes attributes) throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> writer.write("<");<br style="font-size:inherit"> writer.write(qName);<br style="font-size:inherit"> <br style="font-size:inherit"> for (int i = 0; i < attributes.getLength(); i++) {<br style="font-size:inherit"> writer.write(" ");<br style="font-size:inherit"> writer.write(attributes.getQName(i));<br style="font-size:inherit"> writer.write("='");<br style="font-size:inherit"> writer.write(escapeXml(attributes.getValue(i)));<br style="font-size:inherit"> writer.write("'");<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> writer.write(">");<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing start element", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void endElement(String uri, String localName, String qName) throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> writer.write("</");<br style="font-size:inherit"> writer.write(qName);<br style="font-size:inherit"> writer.write(">");<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing end element", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void characters(char[] ch, int start, int length) throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> writer.write(escapeXml(new String(ch, start, length)));<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing characters", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void startDocument() throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> writer.write("<?xml version='1.0' encoding='UTF-8'?>");<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing start document", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> @Override<br style="font-size:inherit"> public void processingInstruction(String target, String data) throws SAXException {<br style="font-size:inherit"> try {<br style="font-size:inherit"> writer.write("<?");<br style="font-size:inherit"> writer.write(target);<br style="font-size:inherit"> if (data != null && !data.isEmpty()) {<br style="font-size:inherit"> writer.write(" ");<br style="font-size:inherit"> writer.write(data);<br style="font-size:inherit"> }<br style="font-size:inherit"> writer.write("?>");<br style="font-size:inherit"> } catch (IOException e) {<br style="font-size:inherit"> throw new SAXException("Error writing processing instruction", e);<br style="font-size:inherit"> }<br style="font-size:inherit"> }<br style="font-size:inherit"> <br style="font-size:inherit"> private String escapeXml(String text) {<br style="font-size:inherit"> return text.replace("&", "&")<br style="font-size:inherit"> .replace("<", "<")<br style="font-size:inherit"> .replace(">", ">")<br style="font-size:inherit"> .replace("'", "'")<br style="font-size:inherit"> .replace("\"", """);<br style="font-size:inherit"> }<br style="font-size:inherit">}</div><div style="font-size:inherit" dir="auto"><br></div><div style="font-size:inherit" dir="auto">// Enjoy!</div><div style="font-size:inherit" dir="auto"><br></div><div style="font-size:inherit" dir="auto">John</div><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Mon, Jan 5, 2026 at 1:43 PM John Carlson <<a href="mailto:yottzumm@gmail.com">yottzumm@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto">What comment parsing requires in Java SAXParser is handling the comment method in a subclass of DefaultHandler2:</div><div dir="auto"><br></div><div dir="auto"><div style="font-size:inherit"><a href="https://docs.oracle.com/javase/8/docs/api/org/xml/sax/ext/DefaultHandler2.html" style="font-size:inherit" target="_blank">https://docs.oracle.com/javase/8/docs/api/org/xml/sax/ext/DefaultHandler2.html</a></div><br></div><div dir="auto"><br></div><div dir="auto">This should be handy enough to replace newlines inside an attribute inside a comment different from other newlines in a comment.</div><div dir="auto"><br></div><div dir="auto">Not ideal yet. Feel free to deploy extra test cases while “code” it up.</div><div dir="auto"><br></div><div dir="auto">John </div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 4, 2026 at 4:01 PM John Carlson <<a href="mailto:yottzumm@gmail.com" target="_blank">yottzumm@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="auto">My current plan for replacing multiple line comments with single lines comments where the comment content are preserved on separate lines is as follows:</div><div dir="auto"><br></div><div dir="auto">1. Write a Java SAX parser to replace newlines with —>\n<!— inside XML comments.</div><div dir="auto"><br></div><div dir="auto">2. Compile with GraalVM native-image into rnl.exe, if not taken.</div><div dir="auto"><br></div><div dir="auto">Write a script to apply to all original .x3d files in my examples. This will call vim or ex (vim command-line), with the command, %!rnl.exe, then save and exit.</div><div dir="auto"><br></div><div dir="auto">If needed, I will include carriage return.</div><div dir="auto"><br></div><div dir="auto">Others would try to make this a stylesheet, but I don’t know if comment() can be split on newlines in XSLT. So I’ll ask <a href="http://claude.ai" target="_blank">claude.ai</a> again!</div><div dir="auto"><br></div><div dir="auto"><div style="font-size:inherit" dir="auto"><span style="font-style:normal;font-weight:400;letter-spacing:normal;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;font-size:inherit;color:rgb(0,0,0)"></span></div><div style="font-size:inherit"><xsl:stylesheet version="2.0" xmlns:xsl="<a href="http://www.w3.org/1999/XSL/Transform" target="_blank">http://www.w3.org/1999/XSL/Transform</a>"><br style="font-size:inherit"> <br style="font-size:inherit"> <xsl:template match="@*|node()"><br style="font-size:inherit"> <xsl:copy><br style="font-size:inherit"> <xsl:apply-templates select="@*|node()"/><br style="font-size:inherit"> </xsl:copy><br style="font-size:inherit"> </xsl:template><br style="font-size:inherit"> <br style="font-size:inherit"> <xsl:template match="comment()"><br style="font-size:inherit"> <xsl:for-each select="tokenize(., '
')"><br style="font-size:inherit"> <xsl:if test="normalize-space(.) != ''"><br style="font-size:inherit"> <xsl:comment><xsl:value-of select="normalize-space(.)"/></xsl:comment><br style="font-size:inherit"> </xsl:if><br style="font-size:inherit"> </xsl:for-each><br style="font-size:inherit"> </xsl:template><br style="font-size:inherit"> <br style="font-size:inherit"></xsl:stylesheet></div><div style="font-size:inherit" dir="auto"><span style="font-style:normal;font-weight:400;letter-spacing:normal;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;font-size:inherit;color:rgb(0,0,0)"></span></div><br></div><div dir="auto">Gobbledegook to me, but maybe someone can use it.</div><div dir="auto"><br></div><div dir="auto">John</div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr"><div style="font-size:inherit" dir="auto"><span>On Sun, Jan 4, 2026 at 3:32 PM John Carlson <<a href="mailto:yottzumm@gmail.com" target="_blank">yottzumm@gmail.com</a>> wrote:</span></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="auto">For example, it nearly requires program to do this in Python or Perl.</div><div dir="auto"><br></div><div dir="auto">Claude.ai reports:</div><div dir="auto"><br></div><div dir="auto"><div style="font-size:inherit"><span style="font-style:normal;font-weight:400;letter-spacing:normal;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;font-size:inherit;color:rgb(0,0,0)">perl -i -0pe </span><span style="font-style:normal;font-weight:400;letter-spacing:normal;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;font-size:inherit;color:rgb(80,161,79)">'s/<!--(.*?)-->/join("\n", map {"<!-- $_ -->"} grep {s|^\s*||; s|\s*$||; $_} split("\n", $1))/gse'</span><span style="font-style:normal;font-weight:400;letter-spacing:normal;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;font-size:inherit;color:rgb(0,0,0)"> file.xml</span></div><br></div><div dir="auto">Obviously I can put this in a shell script, but I like stuff I can understand!</div><div dir="auto"><br></div><div dir="auto">Something in Java would be great! Maybe I’ll write and contribute if it doesn’t already exist?</div><div dir="auto"><br></div><div dir="auto">Seems like a simple SAX parser, frankly!</div><div dir="auto"><br></div><div dir="auto">John</div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 4, 2026 at 3:10 PM John Carlson <<a href="mailto:yottzumm@gmail.com" target="_blank">yottzumm@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="auto">Thanks, it wasn’t clear to me that multi-line comments were not supported in X3D XML encoding. That clears up most of my questions!</div><div dir="auto"><br></div><div dir="auto">I will replace multi-line comments with single-line comments in my examples. Does X3D-Edit support this feature? This would be great, and encourage me to use it!</div><div dir="auto"><br></div><div dir="auto">John</div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 4, 2026 at 2:46 PM Don Brutzman <<a href="mailto:don.brutzman@gmail.com" target="_blank">don.brutzman@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="ltr"><div>For clarity, here is a recap of key points that can be found in prior responses and ticket issues:</div><div><ul><li>Persistent single-line comments are an important feature for X3D model conversions and interoperability.</li><li>You are welcome to use all Python features, including multi-line comments, whenever programming with Python and x3d.py.</li><li>Individual multi-line comments are not supported in a number of file encodings and programming languages. Nor are they defined by X3D Architecture, nor are they supported by several X3D encodings (such as XML and ClassicVRML). Thus multi-line comments are not a required feature.</li><li>Persistent comments in x3d.py Python are already supported via a Comment class.</li><li>Persistent single-line comments are now partially supported by X3dToPython.xslt converter, if present as children of Scene head and grouping nodes. Further future work on content-model representations in the Python class hierarchy may be able to add them elsewhere (e.g. inside Shape Appearance Material geometry etc.</li><li>Tickets are closed after careful review if they are unrepeatable or unactionable. Suggestions and questions about a clearer ticket are usually included.</li></ul><div>Hope this helps.</div></div></div><div dir="ltr"><div><br></div><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div style="color:rgb(34,34,34)"><font face="monospace" style="font-family:monospace;color:rgb(34,34,34)">all the best, Don</font></div><div style="color:rgb(34,34,34)"><font face="monospace" style="font-family:monospace;color:rgb(34,34,34)">-- </font></div><div style="color:rgb(34,34,34)"><font face="monospace" style="font-family:monospace;color:rgb(34,34,34)">X3D Graphics, Maritime Robotics, Distributed Simulation</font></div><div style="color:rgb(34,34,34)"><font face="monospace" style="font-family:monospace;color:rgb(34,34,34)">Relative Motion Consulting <a href="https://RelativeMotion.info" style="font-family:monospace" target="_blank">https://RelativeMotion.info</a></font></div></div></div></div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 4, 2026 at 5:57 AM John Carlson <<a href="mailto:yottzumm@gmail.com" target="_blank">yottzumm@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="auto">Blowing off some steam. Apologies, I probably should work out instead.</div><div dir="auto"><br></div><div dir="auto">I am unsure if it’s easier to output straight line code instead of hierarchical code in Python; multi-line non-persistent comments might become easier in straight line code. I don’t know about comments() in XSLT. In straight line code, wrapping a multi or single line comment in “””…””” is a cinch. Even if newlines need to be added for the Python interpreter. </div><div dir="auto"><span>AFAIK, I’ve already mentioned that straight line code is easier to debug</span><span>, in my mind</span><span>. I think the Java large file discussion is still open; I know hierarchical code is more succinct, and less likely to stack overflow. I think the choices between code structure in Python and Java might be different.</span><br></div><div dir="auto"><br></div><div dir="auto">Persistent multi-line comments are desirable, even if they aren’t present in the archive. We will probably make different choices between hierarchical and straight line code when this feature is available.</div><div dir="auto"><br></div><div dir="auto">Short term hacks like single line comments work until features are introduced. That doesn’t mean tickets should be closed prematurely, or examples outside the archives are invalid.</div><div dir="auto"><br></div><div dir="auto">John </div><div dir="auto"><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 30, 2025 at 3:42 AM John Carlson <<a href="mailto:yottzumm@gmail.com" target="_blank">yottzumm@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div><div dir="auto">Don, thanks for your comment on this ticket that naively adding Comment to a children field might not work. That was one of my intentions on expressing myself so vocally. I apologize for using your time to do this, but I think it revealed a possible design consideration. I think if I had discovered it, it would have gone over like a wet balloon.</div></div><div dir="auto"><br></div><div dir="auto">Thank you again, I will not put comments arbitrarily in the scenegraph for now with my PythonSerializer.js, until you indicate it’s ready! I will leave them out of the scenegraph, as they are now. Since I handle comments generically, I will have to research the nodes you’ve accomplished.</div><div dir="auto"><br></div><div dir="auto">Now, can we have other multi-line comments, not in the persistent scenegraph, in Python translated from .x3d, in the short term, like:</div><div dir="auto"><br></div><div dir="auto">#</div><div dir="auto"># This is comment line 2</div><div dir="auto"># This is comment line 3</div><div dir="auto">#</div><div dir="auto"><br></div><div dir="auto">?</div><div dir="auto"><br></div><div dir="auto"><span>John</span><br></div><div><div dir="auto"><br></div><div dir="auto">From the ticket:</div><div dir="auto"><br></div><div dir="auto">“<div dir="auto" style="font-size:inherit"><div style="margin:0px;padding:0px;border:0px;outline:0px;font-size:inherit;vertical-align:baseline;background:0% 0% repeat rgba(0,0,0,0);font-family:lato,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;text-align:left;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;color:rgb(85,85,85)"><div style="margin:0px;padding:0px;border:0px;outline:0px;font-size:14px;vertical-align:baseline;background:0% 0% repeat rgba(0,0,0,0);font-family:lato,sans-serif"><p style="margin:0px;padding:0px 10px 1em;border:0px;outline:0px;font-size:14px;vertical-align:baseline;background:0% 0% repeat rgba(0,0,0,0);font-family:lato,sans-serif">This is not an easy fix for converting files because Comment is not part of the content model for all nodes. Solving this will probably require extending class _ X3DNode in x3d.py and sorting out possible subclass collisions. It will also be difficult to retain the original order of child nodes and child comments within a parent node.</p><p style="margin:0px;padding:0px 10px 1em;border:0px;outline:0px;font-size:14px;vertical-align:baseline;background:0% 0% repeat rgba(0,0,0,0);font-family:lato,sans-serif">This might be fixable someday - after some effort I got it working for head, Scene, field, fieldValue.. Deferred as future work.</p></div></div><span style="font-family:lato,sans-serif;font-size:inherit;font-style:normal;font-weight:400;letter-spacing:normal;text-align:left;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;float:none;display:inline;background-color:rgb(255,255,255);color:rgb(85,85,85)"> ”</span></div></div><div><br><div class="gmail_quote"></div></div></div><div><div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Dec 29, 2025 at 12:52 PM Don Brutzman <<a href="mailto:don.brutzman@gmail.com" target="_blank">don.brutzman@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div dir="ltr"><div dir="ltr"><div><br></div></div><div dir="ltr"><div><div>I have posted a new ticket to capture this point. Perhaps a useful exemplar, as well.</div><div><ul><li>X3D SourceForge ticket #82 creating persistent comments in python when converting from XML</li><li><a href="https://sourceforge.net/p/x3d/tickets/82/" target="_blank">https://sourceforge.net/p/x3d/tickets/82/</a></li></ul></div><div dir="auto">Hope this helps. Again thanks for your many efforts. Have fun improving X3D! 🤔 👍</div></div></div></div><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div><div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)"><div><p></p></div> <div>
<div>
</div>
</div></blockquote></div></div>
</blockquote></div></div>
</div>
</blockquote></div>
</blockquote></div>
</blockquote></div>
</blockquote></div></div>
</blockquote></div>
</div>
</blockquote></div></div>
</div>
</blockquote></div></div>
</blockquote></div>
</blockquote></div></div>
</blockquote></div></div>
</blockquote></div></div>
</blockquote></div></div>
</blockquote></div></div>