Created
March 18, 2026 09:42
-
-
Save bdbai/deadeb6932257b26d2c39f1b44208eb3 to your computer and use it in GitHub Desktop.
pretty print json in jupyter notebook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # filepath: .\python_notebooks\util\html_display.py | |
| """ | |
| HTML display utilities for rendering Python objects as interactive HTML | |
| Inspired by dotnet interactive's HtmlFormatter | |
| """ | |
| import json | |
| from typing import Any, Dict, List, Union, Optional | |
| import pandas as pd | |
| from IPython.display import HTML, display | |
| from html import escape | |
| # CSS styles for the HTML output | |
| DEFAULT_STYLES = """ | |
| <style> | |
| .dni-code-hint { | |
| font-style: italic; | |
| overflow: hidden; | |
| white-space: nowrap; | |
| color: firebrick; | |
| background: rgba(0, 0, 0, 0.1); | |
| } | |
| .dni-treeview { | |
| white-space: nowrap; | |
| } | |
| .dni-treeview td { | |
| vertical-align: top; | |
| text-align: start; | |
| } | |
| details.dni-treeview { | |
| padding-left: 1em; | |
| } | |
| table td { | |
| text-align: start; | |
| } | |
| table tr { | |
| vertical-align: top; | |
| margin: 0em 0px; | |
| } | |
| table tr td pre { | |
| vertical-align: top !important; | |
| margin: 0em 0px !important; | |
| } | |
| table th { | |
| text-align: start; | |
| } | |
| .dni-plaintext { | |
| margin: 5px 10px; | |
| padding: 5px; | |
| border-radius: 3px; | |
| background-color: #f5f5f5; | |
| } | |
| </style> | |
| """ | |
| def dict_to_html_table(data: Dict) -> str: | |
| """Convert a dictionary to an HTML table""" | |
| if not data: | |
| return "<i>Empty dictionary</i>" | |
| rows = [] | |
| for key, value in data.items(): | |
| formatted_value = format_value_to_html(value) | |
| rows.append(f"<tr><td><strong>{key}</strong></td><td>{formatted_value}</td></tr>") | |
| return f"<table>{''.join(rows)}</table>" | |
| def list_to_html_table(data: List) -> str: | |
| """Convert a list to an HTML table""" | |
| if not data: | |
| return "<i>Empty list</i>" | |
| rows = [] | |
| for i, value in enumerate(data): | |
| formatted_value = format_value_to_html(value) | |
| rows.append(f"<tr><td>{i}</td><td>{formatted_value}</td></tr>") | |
| return f"<table>{''.join(rows)}</table>" | |
| def format_value_to_html(value: Any) -> str: | |
| """Format a value to HTML based on its type""" | |
| if value is None: | |
| return "<span style='color: #888'><null></span>" | |
| elif isinstance(value, (dict, list)) and not value: | |
| return "<span style='color: #888'>empty</span>" | |
| elif isinstance(value, dict): | |
| # Show the full JSON object similar to dotnet interactive | |
| json_str = json.dumps(value, separators=(',', ':')) | |
| return f""" | |
| <details class="dni-treeview"> | |
| <summary><span class="dni-code-hint">{escape(json_str)}</span></summary> | |
| <div>{dict_to_html_table(value)}</div> | |
| </details> | |
| """ | |
| elif isinstance(value, list): | |
| if not value: | |
| return "[]" | |
| # Show full array content similar to dotnet interactive | |
| json_str = json.dumps(value, separators=(',', ':')) | |
| return f""" | |
| <details class="dni-treeview"> | |
| <summary><span class="dni-code-hint">{escape(json_str)}</span></summary> | |
| <div>{list_to_html_table(value)}</div> | |
| </details> | |
| """ | |
| elif isinstance(value, str): | |
| if len(value) > 100: | |
| return f"<span>'{escape(value[:100])}...'</span>" | |
| return f"<span>'{escape(value)}'</span>" | |
| elif isinstance(value, (int, float, bool)): | |
| return f"<span>{value}</span>" | |
| else: | |
| return f"<span>{str(value)}</span>" | |
| def json_to_html(data: Union[Dict, List, Any]) -> str: | |
| """ | |
| Convert JSON data to interactive HTML with expandable sections | |
| Args: | |
| data: The data to convert to HTML | |
| Returns: | |
| HTML string representation of the data | |
| """ | |
| html = DEFAULT_STYLES | |
| html += "<div class='dni-treeview-root'>" | |
| if isinstance(data, dict): | |
| html += dict_to_html_table(data) | |
| elif isinstance(data, list): | |
| html += list_to_html_table(data) | |
| else: | |
| html += f"<pre class='dni-plaintext'>{escape(json.dumps(data, indent=2))}</pre>" | |
| html += "</div>" | |
| return html | |
| def display_as_html(data: Any) -> None: | |
| """ | |
| Display data as interactive HTML in a Jupyter notebook | |
| Args: | |
| data: The data to display | |
| """ | |
| display(HTML(json_to_html(data))) | |
| def dataframe_with_json_explorer(df: pd.DataFrame) -> pd.DataFrame: | |
| """ | |
| Add an 'Explore' button to complex JSON columns in a DataFrame | |
| Args: | |
| df: The DataFrame to enhance | |
| Returns: | |
| Enhanced DataFrame with exploration buttons | |
| """ | |
| def _add_explorer_buttons(df): | |
| df_copy = df.copy() | |
| for col in df.columns: | |
| # Check the first non-null value | |
| non_null = df[col].dropna() | |
| if len(non_null) == 0: | |
| continue | |
| sample = non_null.iloc[0] | |
| # Check if column contains complex structures | |
| if isinstance(sample, (dict, list)) or str(sample).startswith('{') or str(sample).startswith('['): | |
| # Create exploration buttons for this column | |
| def make_button(val): | |
| if pd.isna(val): | |
| return val | |
| try: | |
| if isinstance(val, str) and (val.startswith('{') or val.startswith('[')): | |
| val = json.loads(val) | |
| button_id = f"btn_{hash(str(val))}" | |
| return f""" | |
| <button onclick=" | |
| $('#{button_id}').toggle(); | |
| $(this).text($(this).text() == 'Explore' ? 'Hide' : 'Explore'); | |
| ">Explore</button> | |
| <div id="{button_id}" style="display: none;"> | |
| {format_value_to_html(val)} | |
| </div> | |
| """ | |
| except: | |
| return val | |
| # Apply button creation to each value in the column | |
| df_copy[col] = df_copy[col].apply(make_button) | |
| return df_copy | |
| # For Jupyter notebook, we need to ensure HTML is properly displayed | |
| pd.set_option('display.max_colwidth', None) | |
| # Return DataFrame with HTML formatting applied | |
| return _add_explorer_buttons(df) | |
| def auto_html_display(data: Any) -> Any: | |
| """ | |
| Automatically display data in the best format | |
| Args: | |
| data: The data to display | |
| Returns: | |
| Appropriate HTML visualization or DataFrame | |
| """ | |
| if isinstance(data, pd.DataFrame): | |
| return dataframe_with_json_explorer(data) | |
| elif isinstance(data, (dict, list)): | |
| display_as_html(data) | |
| return None | |
| return data |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment