{"id":72769,"date":"2018-01-22T22:00:57","date_gmt":"2018-01-22T20:00:57","guid":{"rendered":"https:\/\/www.javacodegeeks.com\/?p=72769"},"modified":"2018-01-22T11:01:11","modified_gmt":"2018-01-22T09:01:11","slug":"converting-html-richtextstring-apache-poi","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html","title":{"rendered":"Converting HTML to RichTextString for Apache POI"},"content":{"rendered":"<h2>1. Overview<\/h2>\n<p>In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a <strong>RichText representation of the HTML<\/strong> that was provided. To generate the Microsoft Excel Workbook, we will be using <strong>Apache POI<\/strong>. To analyze the HTML, we will be using Jericho.<\/p>\n<p>The full source code for this tutorial is available on <a href=\"https:\/\/github.com\/michaelcgood\/HTML-to-Apache-POI-RichTextString\" target=\"_blank\" rel=\"noopener\">Github<\/a>.<\/p>\n<h2>2. What is Jericho?<\/h2>\n<p><a href=\"http:\/\/jericho.htmlparser.net\/docs\/index.html\">Jericho is a java library<\/a> that allows analysis and manipulation of parts of an HTML document, including server-side tags, while reproducing verbatim any unrecognized or invalid HTML. It also provides high-level HTML form manipulation functions.\u00a0It is an open source library released under the following licenses:\u00a0<a href=\"http:\/\/www.eclipse.org\/legal\/epl-v10.html\">Eclipse Public License (EPL)<\/a>,\u00a0<a href=\"http:\/\/www.gnu.org\/copyleft\/lesser.html\">GNU Lesser General Public License (LGPL)<\/a>, and\u00a0<a href=\"http:\/\/www.apache.org\/licenses\/LICENSE-2.0.html\">Apache License<\/a>.<\/p>\n<p>I found Jericho to be very easy to use for achieving my goal of converting HTML to RichText.<\/p>\n<h2>3. pom.xml<\/h2>\n<p>Here are the required dependencies for the application we are building. Please take note that for this application we have to use <strong>Java 9<\/strong>. This is because of a <a href=\"https:\/\/docs.oracle.com\/javase\/9\/docs\/api\/java\/util\/regex\/Matcher.html#appendReplacement-java.lang.StringBuilder-java.lang.String-\" target=\"_blank\" rel=\"noopener\">java.util.regex appendReplacement method<\/a> we use that has only been available since Java 9.<\/p>\n<pre class=\"brush:xml\">&lt;parent&gt;\r\n\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n\t&lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\r\n\t&lt;version&gt;1.5.9.RELEASE&lt;\/version&gt;\r\n\t&lt;relativePath \/&gt; &lt;!-- lookup parent from repository --&gt;\r\n&lt;\/parent&gt;\r\n\r\n&lt;properties&gt;\r\n\t&lt;project.build.sourceEncoding&gt;UTF-8&lt;\/project.build.sourceEncoding&gt;\r\n\t&lt;project.reporting.outputEncoding&gt;UTF-8&lt;\/project.reporting.outputEncoding&gt;\r\n\t&lt;java.version&gt;9&lt;\/java.version&gt;\r\n&lt;\/properties&gt;\r\n\r\n&lt;dependencies&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;spring-boot-starter-batch&lt;\/artifactId&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;\/artifactId&gt;\r\n\t&lt;\/dependency&gt;\r\n\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;com.h2database&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;h2&lt;\/artifactId&gt;\r\n\t\t&lt;scope&gt;runtime&lt;\/scope&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n\t\t&lt;scope&gt;test&lt;\/scope&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;!-- https:\/\/mvnrepository.com\/artifact\/org.apache.commons\/commons-lang3 --&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.apache.commons&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;commons-lang3&lt;\/artifactId&gt;\r\n\t\t&lt;version&gt;3.7&lt;\/version&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.springframework.batch&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;spring-batch-test&lt;\/artifactId&gt;\r\n\t\t&lt;scope&gt;test&lt;\/scope&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.apache.poi&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;poi&lt;\/artifactId&gt;\r\n\t\t&lt;version&gt;3.15&lt;\/version&gt;\r\n\t&lt;\/dependency&gt;\r\n\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.apache.poi&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;poi-ooxml&lt;\/artifactId&gt;\r\n\t\t&lt;version&gt;3.15&lt;\/version&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;!-- https:\/\/mvnrepository.com\/artifact\/net.htmlparser.jericho\/jericho-html --&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;net.htmlparser.jericho&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;jericho-html&lt;\/artifactId&gt;\r\n\t\t&lt;version&gt;3.4&lt;\/version&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;spring-boot-configuration-processor&lt;\/artifactId&gt;\r\n\t\t&lt;optional&gt;true&lt;\/optional&gt;\r\n\t&lt;\/dependency&gt;\r\n\t&lt;!-- legacy html allow --&gt;\r\n\t&lt;dependency&gt;\r\n\t\t&lt;groupId&gt;net.sourceforge.nekohtml&lt;\/groupId&gt;\r\n\t\t&lt;artifactId&gt;nekohtml&lt;\/artifactId&gt;\r\n\t&lt;\/dependency&gt;\r\n&lt;\/dependencies&gt;<\/pre>\n<h2>4. Web page \u2013 Thymeleaf<\/h2>\n<p>We use Thymeleaf to create a basic webpage that has a form with a textarea. The source code for Thymeleaf page is <a href=\"https:\/\/github.com\/michaelcgood\/HTML-to-Apache-POI-RichTextString\/blob\/master\/src\/main\/resources\/templates\/index.html\" target=\"_blank\" rel=\"noopener\">available here on Github<\/a>. This textarea could be replaced with a RichText Editor if we like, such as CKEditor. We just must be mindful to make the <em>data<\/em> for AJAX correct, using an appropriate <em>setData<\/em> method. There is a previous tutorial about CKeditor titled <a href=\"https:\/\/www.javacodegeeks.com\/2017\/11\/ajax-ckeditor-spring-boot.html\" target=\"_blank\" rel=\"noopener\">AJAX with CKEditor in Spring Boot<\/a>.<\/p>\n<h2>5. Controller<\/h2>\n<p>In our controller, we Autowire <em>JobLauncher<\/em> and a Spring Batch job we are going to create called <em>GenerateExcel<\/em>. Autowiring these two classes allow us to run the Spring Batch Job <em>GenerateExcel<\/em> on demand when a POST request is sent to <em>\u201c\/export\u201d<\/em>.<\/p>\n<p>Another thing to note is that to ensure that the Spring Batch job will run more than once we include unique parameters with this code: <em>addLong(\u201cuniqueness\u201d, System.nanoTime()).toJobParameters()<\/em>. An error may occur if we do not include unique parameters because only unique <em>JobInstances<\/em> may be created and executed, and Spring Batch has no way of distinguishing between the first and second <em>JobInstance <\/em>otherwise.<\/p>\n<pre class=\"brush:java\">@Controller\r\npublic class WebController {\r\n\r\n    private String currentContent;\r\n\r\n    @Autowired\r\n    JobLauncher jobLauncher;\r\n    \r\n    @Autowired\r\n    GenerateExcel exceljob; \r\n\r\n    @GetMapping(\"\/\")\r\n    public ModelAndView getHome() {\r\n        ModelAndView modelAndView = new ModelAndView(\"index\");\r\n        return modelAndView;\r\n\r\n    }\r\n    \r\n\r\n    @PostMapping(\"\/export\")\r\n    public String postTheFile(@RequestBody String body, RedirectAttributes redirectAttributes, Model model)\r\n        throws IOException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException {\r\n\r\n\r\n        setCurrentContent(body);\r\n\r\n        Job job = exceljob.ExcelGenerator();\r\n        jobLauncher.run(job, new JobParametersBuilder().addLong(\"uniqueness\", System.nanoTime()).toJobParameters()\r\n            );\r\n\r\n        return \"redirect:\/\";\r\n    }\r\n\r\n    \/\/standard getters and setters\r\n\r\n}<\/pre>\n<h2>6. Batch Job<\/h2>\n<p>In Step1 of our Batch job, we call the getCurrentContent() method to get the content that was passed into the Thymeleaf form, create a new XSSFWorkbook, specify an arbitrary Microsoft Excel Sheet tab name, and then pass all three variables into the createWorksheet method that we will be making in the next step of our tutorial :<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\"brush:java\">@Configuration\r\n@EnableBatchProcessing\r\n@Lazy\r\npublic class GenerateExcel {\r\n    \r\n    List&lt;String&gt; docIds = new ArrayList&lt;String&gt;();\r\n\r\n    @Autowired\r\n    private JobBuilderFactory jobBuilderFactory;\r\n\r\n    @Autowired\r\n    private StepBuilderFactory stepBuilderFactory;\r\n\r\n    @Autowired\r\n    WebController webcontroller;\r\n    \r\n    @Autowired\r\n    CreateWorksheet createexcel;\r\n\r\n    @Bean\r\n    public Step step1() {\r\n        return stepBuilderFactory.get(\"step1\")\r\n            .tasklet(new Tasklet() {\r\n                @Override\r\n                public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception, JSONException {\r\n\r\n                    String content = webcontroller.getCurrentContent();\r\n                    \r\n                    System.out.println(\"content is ::\" + content);\r\n                    Workbook wb = new XSSFWorkbook();\r\n                    String tabName = \"some\";\r\n                    createexcel.createWorkSheet(wb, content, tabName);\r\n\r\n                    return RepeatStatus.FINISHED;\r\n                }\r\n            })\r\n            .build();\r\n    }\r\n\r\n    @Bean\r\n    public Job ExcelGenerator() {\r\n        return jobBuilderFactory.get(\"ExcelGenerator\")\r\n            .start(step1())\r\n            .build();\r\n\r\n    }\r\n\r\n}<\/pre>\n<p>We have covered Spring Batch in other tutorials such as <a href=\"http:\/\/michaelcgood.com\/xml-json-raw-mongodb-spring-batch\/\" target=\"_blank\" rel=\"noopener\">Converting XML to JSON + Spring Batch<\/a> and <a href=\"https:\/\/www.javacodegeeks.com\/2017\/10\/spring-batch-csv-processing.html\" target=\"_blank\" rel=\"noopener\">Spring Batch CSV Processing<\/a>.<\/p>\n<h2>7. Excel Creation Service<\/h2>\n<p>We use a variety of classes to create our Microsoft Excel file. Order matters when dealing with converting HTML to RichText, so this will be a focus.<\/p>\n<h3>7.1 RichTextDetails<\/h3>\n<p>A class with two parameters: a String that will have our contents that will become RichText and a font map.<\/p>\n<pre class=\"brush:java\">public class RichTextDetails {\r\n    private String richText;\r\n    private Map&lt;Integer, Font&gt; fontMap;\r\n    \/\/standard getters and setters\r\n    @Override\r\n    public int hashCode() {\r\n     \r\n        \/\/ The goal is to have a more efficient hashcode than standard one.\r\n        return richText.hashCode();\r\n    }<\/pre>\n<h3>7.2 RichTextInfo<\/h3>\n<p>A POJO that will keep track of the location of the RichText and what not:<\/p>\n<pre class=\"brush:java\">public class RichTextInfo {\r\n    private int startIndex;\r\n    private int endIndex;\r\n    private STYLES fontStyle;\r\n    private String fontValue;\r\n    \/\/ standard getters and setters, and the like<\/pre>\n<h3>7.3 Styles<\/h3>\n<p>A enum to contains HTML tags that we want to process. We can add to this as necessary:<\/p>\n<pre class=\"brush:java\">public enum STYLES {\r\n    BOLD(\"b\"), \r\n    EM(\"em\"), \r\n    STRONG(\"strong\"), \r\n    COLOR(\"color\"), \r\n    UNDERLINE(\"u\"), \r\n    SPAN(\"span\"), \r\n    ITALLICS(\"i\"), \r\n    UNKNOWN(\"unknown\"),\r\n    PRE(\"pre\");\r\n    \/\/ standard getters and setters<\/pre>\n<h3>7.4 TagInfo<\/h3>\n<p>A POJO to keep track of tag info:<\/p>\n<pre class=\"brush:java\">public class TagInfo {\r\n    private String tagName;\r\n    private String style;\r\n    private int tagType;\r\n    \/\/ standard getters and setters<\/pre>\n<h3>7.5 HTML to RichText<\/h3>\n<p>This is not a small class, so let\u2019s break it down by method.<\/p>\n<p>Essentially, we are surrounding any arbitrary HTML with a <em>div<\/em> tag, so we know what we are looking for. Then we look for all elements within the <em>div<\/em> tag, add each to an ArrayList of RichTextDetails , and then pass the whole ArrayList to the mergeTextDetails method. mergeTextDetails returns RichtextString, which is what we need to set a cell value:<\/p>\n<pre class=\"brush:java\">public RichTextString fromHtmlToCellValue(String html, Workbook workBook){\r\n       Config.IsHTMLEmptyElementTagRecognised = true;\r\n       \r\n       Matcher m = HEAVY_REGEX.matcher(html);\r\n       String replacedhtml =  m.replaceAll(\"\");\r\n       StringBuilder sb = new StringBuilder();\r\n       sb.insert(0, \"&lt;div&gt;\");\r\n       sb.append(replacedhtml);\r\n       sb.append(\"&lt;\/div&gt;\");\r\n       String newhtml = sb.toString();\r\n       Source source = new Source(newhtml);\r\n       List&lt;RichTextDetails&gt; cellValues = new ArrayList&lt;RichTextDetails&gt;();\r\n       for(Element el : source.getAllElements(\"div\")){\r\n           cellValues.add(createCellValue(el.toString(), workBook));\r\n       }\r\n       RichTextString cellValue = mergeTextDetails(cellValues);\r\n\r\n       \r\n       return cellValue;\r\n   }<\/pre>\n<p>As we saw above, we pass an ArrayList of RichTextDetails in this method. Jericho has a setting that takes boolean value to recognize empty tag elements such as<br \/>\n: Config.IsHTMLEmptyElementTagRecognised. This can be important when dealing with online rich text editors, so we set this to true. Because we need to keep track of the order of the elements, we use a LinkedHashMap instead of a HashMap.<\/p>\n<pre class=\"brush:java\">private static RichTextString mergeTextDetails(List&lt;RichTextDetails&gt; cellValues) {\r\n        Config.IsHTMLEmptyElementTagRecognised = true;\r\n        StringBuilder textBuffer = new StringBuilder();\r\n        Map&lt;Integer, Font&gt; mergedMap = new LinkedHashMap&lt;Integer, Font&gt;(550, .95f);\r\n        int currentIndex = 0;\r\n        for (RichTextDetails richTextDetail : cellValues) {\r\n            \/\/textBuffer.append(BULLET_CHARACTER + \" \");\r\n            currentIndex = textBuffer.length();\r\n            for (Entry&lt;Integer, Font&gt; entry : richTextDetail.getFontMap()\r\n                .entrySet()) {\r\n                mergedMap.put(entry.getKey() + currentIndex, entry.getValue());\r\n            }\r\n            textBuffer.append(richTextDetail.getRichText())\r\n                .append(NEW_LINE);\r\n        }\r\n\r\n        RichTextString richText = new XSSFRichTextString(textBuffer.toString());\r\n        for (int i = 0; i &lt; textBuffer.length(); i++) {\r\n            Font currentFont = mergedMap.get(i);\r\n            if (currentFont != null) {\r\n                richText.applyFont(i, i + 1, currentFont);\r\n            }\r\n        }\r\n        return richText;\r\n    }<\/pre>\n<p>As mentioned above, we are using Java 9 in order to use StringBuilder with the <em>java.util.regex.Matcher.appendReplacement<\/em>. Why? Well that\u2019s because StringBuffer slower than StringBuilder for operations. StringBuffer functions are synchronized for thread safety and thus slower.<\/p>\n<p>We are using Deque instead of Stack because a more complete and consistent set of LIFO stack operations is provided by the Deque interface:<\/p>\n<pre class=\"brush:java\">static RichTextDetails createCellValue(String html, Workbook workBook) {\r\n        Config.IsHTMLEmptyElementTagRecognised  = true;\r\n        Source source = new Source(html);\r\n        Map&lt;String, TagInfo&gt; tagMap = new LinkedHashMap&lt;String, TagInfo&gt;(550, .95f);\r\n        for (Element e : source.getChildElements()) {\r\n            getInfo(e, tagMap);\r\n        }\r\n\r\n        StringBuilder sbPatt = new StringBuilder();\r\n        sbPatt.append(\"(\").append(StringUtils.join(tagMap.keySet(), \"|\")).append(\")\");\r\n        String patternString = sbPatt.toString();\r\n        Pattern pattern = Pattern.compile(patternString);\r\n        Matcher matcher = pattern.matcher(html);\r\n\r\n        StringBuilder textBuffer = new StringBuilder();\r\n        List&lt;RichTextInfo&gt; textInfos = new ArrayList&lt;RichTextInfo&gt;();\r\n        ArrayDeque&lt;RichTextInfo&gt; richTextBuffer = new ArrayDeque&lt;RichTextInfo&gt;();\r\n        while (matcher.find()) {\r\n            matcher.appendReplacement(textBuffer, \"\");\r\n            TagInfo currentTag = tagMap.get(matcher.group(1));\r\n            if (START_TAG == currentTag.getTagType()) {\r\n                richTextBuffer.push(getRichTextInfo(currentTag, textBuffer.length(), workBook));\r\n            } else {\r\n                if (!richTextBuffer.isEmpty()) {\r\n                    RichTextInfo info = richTextBuffer.pop();\r\n                    if (info != null) {\r\n                        info.setEndIndex(textBuffer.length());\r\n                        textInfos.add(info);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        matcher.appendTail(textBuffer);\r\n        Map&lt;Integer, Font&gt; fontMap = buildFontMap(textInfos, workBook);\r\n\r\n        return new RichTextDetails(textBuffer.toString(), fontMap);\r\n    }<\/pre>\n<p>We can see where RichTextInfo comes in to use here:<\/p>\n<pre class=\"brush:java\">private static Map&lt;Integer, Font&gt; buildFontMap(List&lt;RichTextInfo&gt; textInfos, Workbook workBook) {\r\n        Map&lt;Integer, Font&gt; fontMap = new LinkedHashMap&lt;Integer, Font&gt;(550, .95f);\r\n\r\n        for (RichTextInfo richTextInfo : textInfos) {\r\n            if (richTextInfo.isValid()) {\r\n                for (int i = richTextInfo.getStartIndex(); i &lt; richTextInfo.getEndIndex(); i++) {\r\n                    fontMap.put(i, mergeFont(fontMap.get(i), richTextInfo.getFontStyle(), richTextInfo.getFontValue(), workBook));\r\n                }\r\n            }\r\n        }\r\n\r\n        return fontMap;\r\n    }<\/pre>\n<p>Where we use STYLES enum:<\/p>\n<pre class=\"brush:java\">private static Font mergeFont(Font font, STYLES fontStyle, String fontValue, Workbook workBook) {\r\n        if (font == null) {\r\n            font = workBook.createFont();\r\n        }\r\n\r\n        switch (fontStyle) {\r\n        case BOLD:\r\n        case EM:\r\n        case STRONG:\r\n            font.setBoldweight(Font.BOLDWEIGHT_BOLD);\r\n            break;\r\n        case UNDERLINE:\r\n            font.setUnderline(Font.U_SINGLE);\r\n            break;\r\n        case ITALLICS:\r\n            font.setItalic(true);\r\n            break;\r\n        case PRE:\r\n            font.setFontName(\"Courier New\");\r\n        case COLOR:\r\n            if (!isEmpty(fontValue)) {\r\n\r\n                font.setColor(IndexedColors.BLACK.getIndex());\r\n            }\r\n            break;\r\n        default:\r\n            break;\r\n        }\r\n\r\n        return font;\r\n    }<\/pre>\n<p>We are making use of the TagInfo class to track the current tag:<\/p>\n<pre class=\"brush:java\">private static RichTextInfo getRichTextInfo(TagInfo currentTag, int startIndex, Workbook workBook) {\r\n        RichTextInfo info = null;\r\n        switch (STYLES.fromValue(currentTag.getTagName())) {\r\n        case SPAN:\r\n            if (!isEmpty(currentTag.getStyle())) {\r\n                for (String style : currentTag.getStyle()\r\n                    .split(\";\")) {\r\n                    String[] styleDetails = style.split(\":\");\r\n                    if (styleDetails != null &amp;&amp; styleDetails.length &gt; 1) {\r\n                        if (\"COLOR\".equalsIgnoreCase(styleDetails[0].trim())) {\r\n                            info = new RichTextInfo(startIndex, -1, STYLES.COLOR, styleDetails[1]);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            break;\r\n        default:\r\n            info = new RichTextInfo(startIndex, -1, STYLES.fromValue(currentTag.getTagName()));\r\n            break;\r\n        }\r\n        return info;\r\n    }<\/pre>\n<p>We process the HTML tags:<\/p>\n<pre class=\"brush:java\">private static void getInfo(Element e, Map&lt;String, TagInfo&gt; tagMap) {\r\n        tagMap.put(e.getStartTag()\r\n            .toString(),\r\n            new TagInfo(e.getStartTag()\r\n                .getName(), e.getAttributeValue(\"style\"), START_TAG));\r\n        if (e.getChildElements()\r\n            .size() &gt; 0) {\r\n            List&lt;Element&gt; children = e.getChildElements();\r\n            for (Element child : children) {\r\n                getInfo(child, tagMap);\r\n            }\r\n        }\r\n        if (e.getEndTag() != null) {\r\n            tagMap.put(e.getEndTag()\r\n                .toString(),\r\n                new TagInfo(e.getEndTag()\r\n                    .getName(), END_TAG));\r\n        } else {\r\n            \/\/ Handling self closing tags\r\n            tagMap.put(e.getStartTag()\r\n                .toString(),\r\n                new TagInfo(e.getStartTag()\r\n                    .getName(), END_TAG));\r\n        }\r\n    }<\/pre>\n<h3>7.6 Create Worksheet<\/h3>\n<p>Using StringBuilder, I create a String that is going to written to FileOutPutStream. In a real application this should be user defined. I appended my folder path and filename on two different lines. Please change the file path to your own.<\/p>\n<p><em>sheet.createRow(0)<\/em> creates a row on the very first line and <em>dataRow.createCell(0)<\/em> creates a cell in column A of the row.<\/p>\n<pre class=\"brush:java\">public void createWorkSheet(Workbook wb, String content, String tabName) {\r\n        StringBuilder sbFileName = new StringBuilder();\r\n        sbFileName.append(\"\/Users\/mike\/javaSTS\/michaelcgood-apache-poi-richtext\/\");\r\n        sbFileName.append(\"myfile.xlsx\");\r\n        String fileMacTest = sbFileName.toString();\r\n        try {\r\n            this.fileOut = new FileOutputStream(fileMacTest);\r\n        } catch (FileNotFoundException ex) {\r\n            Logger.getLogger(CreateWorksheet.class.getName())\r\n                .log(Level.SEVERE, null, ex);\r\n        }\r\n\r\n        Sheet sheet = wb.createSheet(tabName); \/\/ Create new sheet w\/ Tab name\r\n\r\n        sheet.setZoom(85); \/\/ Set sheet zoom: 85%\r\n        \r\n\r\n        \/\/ content rich text\r\n        RichTextString contentRich = null;\r\n        if (content != null) {\r\n            contentRich = htmlToExcel.fromHtmlToCellValue(content, wb);\r\n        }\r\n\r\n\r\n        \/\/ begin insertion of values into cells\r\n        Row dataRow = sheet.createRow(0);\r\n        Cell A = dataRow.createCell(0); \/\/ Row Number\r\n        A.setCellValue(contentRich);\r\n        sheet.autoSizeColumn(0);\r\n        \r\n        \r\n        try {\r\n            \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n            \/\/ Write the output to a file\r\n            wb.write(fileOut);\r\n            fileOut.close();\r\n        } catch (IOException ex) {\r\n            Logger.getLogger(CreateWorksheet.class.getName())\r\n                .log(Level.SEVERE, null, ex);\r\n        }\r\n\r\n\r\n    }<\/pre>\n<h2>8. Demo<\/h2>\n<p>We visit <em>localhost:8080<\/em>.<\/p>\n<p>We input some text with some HTML:<br \/>\n<a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/apache-poi-richtext-example.png\"><img decoding=\"async\" class=\"aligncenter size-large wp-image-72782\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/apache-poi-richtext-example-1024x420.png\" alt=\"\" width=\"620\" height=\"254\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/apache-poi-richtext-example.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/apache-poi-richtext-example-300x123.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/apache-poi-richtext-example-768x315.png 768w\" sizes=\"(max-width: 620px) 100vw, 620px\" \/><\/a><\/p>\n<p>We open up our excel file and see the RichText we created:<br \/>\n<a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/excel-apache-poi-screenshot.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-72783\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/excel-apache-poi-screenshot.png\" alt=\"\" width=\"635\" height=\"370\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/excel-apache-poi-screenshot.png 635w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2018\/01\/excel-apache-poi-screenshot-300x175.png 300w\" sizes=\"(max-width: 635px) 100vw, 635px\" \/><\/a><\/p>\n<h2>9. Conclusion<\/h2>\n<p>We can see it is not trivial to convert HTML to Apache POI\u2019s RichTextString class; however, for business applications converting HTML to RichTextString can be essential because readability is important in Microsoft Excel files. There\u2019s likely room to improve upon the performance of the application we build, but we covered the foundation of building such an application .<\/p>\n<p>The full source code is available on <a href=\"https:\/\/github.com\/michaelcgood\/HTML-to-Apache-POI-RichTextString\" target=\"_blank\" rel=\"noopener\">Github.<\/a><\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td>Published on Java Code Geeks with permission by Michael Good, partner at our <a href=\"http:\/\/www.javacodegeeks.com\/join-us\/jcg\/\" target=\"_blank\" rel=\"noopener\">JCG program<\/a>. See the original article here: <a href=\"http:\/\/michaelcgood.com\/richtextstring-apache-poi-html\/\" target=\"_blank\" rel=\"noopener\">Converting HTML to RichTextString for Apache POI<\/a><\/p>\n<p>Opinions expressed by Java Code Geeks contributors are their own.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText representation of the HTML that was provided. To generate the Microsoft Excel Workbook, we will be using Apache POI. To analyze the HTML, we will be using Jericho. The &hellip;<\/p>\n","protected":false},"author":5558,"featured_media":240,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[603,30],"class_list":["post-72769","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-apache-poi","tag-spring"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Converting HTML to RichTextString for Apache POI - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Converting HTML to RichTextString for Apache POI - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2018-01-22T20:00:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Michael Good\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Michael Good\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html\"},\"author\":{\"name\":\"Michael Good\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/d13cc729556b91450ae21878a82139ed\"},\"headline\":\"Converting HTML to RichTextString for Apache POI\",\"datePublished\":\"2018-01-22T20:00:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html\"},\"wordCount\":984,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"keywords\":[\"Apache POI\",\"Spring\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html\",\"name\":\"Converting HTML to RichTextString for Apache POI - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"datePublished\":\"2018-01-22T20:00:57+00:00\",\"description\":\"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"spring-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2018\\\/01\\\/converting-html-richtextstring-apache-poi.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Converting HTML to RichTextString for Apache POI\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/d13cc729556b91450ae21878a82139ed\",\"name\":\"Michael Good\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g\",\"caption\":\"Michael Good\"},\"description\":\"Michael is a software engineer located in the Washington DC area that is interested in Java, cyber security, and open source technologies. Follow his personal blog to read more from Michael.\",\"sameAs\":[\"http:\\\/\\\/www.michaelcgood.com\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/michael-good\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Converting HTML to RichTextString for Apache POI - Java Code Geeks","description":"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html","og_locale":"en_US","og_type":"article","og_title":"Converting HTML to RichTextString for Apache POI - Java Code Geeks","og_description":"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText","og_url":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2018-01-22T20:00:57+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","type":"image\/jpeg"}],"author":"Michael Good","twitter_card":"summary_large_image","twitter_creator":"@javacodegeeks","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Michael Good","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html"},"author":{"name":"Michael Good","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/d13cc729556b91450ae21878a82139ed"},"headline":"Converting HTML to RichTextString for Apache POI","datePublished":"2018-01-22T20:00:57+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html"},"wordCount":984,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","keywords":["Apache POI","Spring"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html","url":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html","name":"Converting HTML to RichTextString for Apache POI - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","datePublished":"2018-01-22T20:00:57+00:00","description":"1. Overview In this tutorial, we will be building an application that takes HTML as an input and creates a Microsoft Excel Workbook with a RichText","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","width":150,"height":150,"caption":"spring-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2018\/01\/converting-html-richtextstring-apache-poi.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Converting HTML to RichTextString for Apache POI"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/d13cc729556b91450ae21878a82139ed","name":"Michael Good","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/dc6ef7dbff80afe08a3cdc3b0677aaa26021085e041ce1873dc2141bc581b623?s=96&d=mm&r=g","caption":"Michael Good"},"description":"Michael is a software engineer located in the Washington DC area that is interested in Java, cyber security, and open source technologies. Follow his personal blog to read more from Michael.","sameAs":["http:\/\/www.michaelcgood.com"],"url":"https:\/\/www.javacodegeeks.com\/author\/michael-good"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/72769","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/5558"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=72769"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/72769\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/240"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=72769"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=72769"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=72769"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}