Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add option to force clubbing anyways #58

Open

Description

Hi team! How have you all been?

MOTIVATION

I would like to share a feature that will add some kind of "forced" implementation of clubbing.. so that when you know your json should look like a table, it will come up with a table anyways...

I did some modding myself only to share this idea...

MODDING

  • added flags for clubbing in the function "convert"
  • added condition for applying the selected clubbing flag in the function "convert_list"
  • added new function "forced_column_headers_from_list_of_dicts" to get all possible column_headers in list of jsons..

CODE

# -*- coding: utf-8 -*-
"""
JSON 2 HTML Converter
=====================

(c) Varun Malhotra 2013
Source Code: https://github.com/softvar/json2html


Contributors:
-------------
1. Michel Müller (@muellermichel), https://github.com/softvar/json2html/pull/2
2. Daniel Lekic (@lekic), https://github.com/softvar/json2html/pull/17

LICENSE: MIT
--------
"""
import sys
if sys.version_info[:2] < (2, 7):
 from ordereddict import OrderedDict
 import simplejson as json_parser
else:
 from collections import OrderedDict
 import json as json_parser
if sys.version_info[:2] < (3, 0):
 from cgi import escape as html_escape
 text = unicode
 text_types = (unicode, str)
else:
 from html import escape as html_escape
 text = str
 text_types = (str,)
class Json2Html:
 def convert(self, json="", table_attributes='border="1"', clubbing='simple', encode=False, escape=True):
 """
 Convert JSON to HTML Table format
 """
 if clubbing not in ['none','simple','forced']:
 raise ValueError('Clubbing must be one of ["none","simple","forced"]')
 # table attributes such as class, id, data-attr-*, etc.
 # eg: table_attributes = 'class = "table table-bordered sortable"'
 self.table_init_markup = "<table %s>" % table_attributes
 self.clubbing = clubbing
 self.escape = escape
 json_input = None
 if not json:
 json_input = {}
 elif type(json) in text_types:
 try:
 json_input = json_parser.loads(json, object_pairs_hook=OrderedDict)
 except ValueError as e:
 #so the string passed here is actually not a json string
 # - let's analyze whether we want to pass on the error or use the string as-is as a text node
 if u"Expecting property name" in text(e):
 #if this specific json loads error is raised, then the user probably actually wanted to pass json, but made a mistake
 raise e
 json_input = json
 else:
 json_input = json
 converted = self.convert_json_node(json_input)
 if encode:
 return converted.encode('ascii', 'xmlcharrefreplace')
 return converted
 def column_headers_from_list_of_dicts(self, json_input):
 """
 This method is required to implement clubbing.
 It tries to come up with column headers for your input
 """
 if not json_input \
 or not hasattr(json_input, '__getitem__') \
 or not hasattr(json_input[0], 'keys'):
 return None
 column_headers = json_input[0].keys()
 for entry in json_input:
 if not hasattr(entry, 'keys') \
 or not hasattr(entry, '__iter__') \
 or len(entry.keys()) != len(column_headers):
 return None
 for header in column_headers:
 if header not in entry:
 return None
 return column_headers
 
 def forced_column_headers_from_list_of_dicts(self, json_input):
 """
 This method is required to implement clubbing.
 It tries to come up with column headers for your input
 """
 if not json_input \
 or not hasattr(json_input, '__getitem__') \
 or not hasattr(json_input[0], 'keys'):
 return None
 column_headers = []
 for i in json_input:
 column_headers = column_headers+list(i.keys())
 column_headers = set(column_headers)
 return column_headers
 def convert_json_node(self, json_input):
 """
 Dispatch JSON input according to the outermost type and process it
 to generate the super awesome HTML format.
 We try to adhere to duck typing such that users can just pass all kinds
 of funky objects to json2html that *behave* like dicts and lists and other
 basic JSON types.
 """
 if type(json_input) in text_types:
 if self.escape:
 return html_escape(text(json_input))
 else:
 return text(json_input)
 if hasattr(json_input, 'items'):
 return self.convert_object(json_input)
 if hasattr(json_input, '__iter__') and hasattr(json_input, '__getitem__'):
 return self.convert_list(json_input)
 return text(json_input)
 def convert_list(self, list_input):
 """
 Iterate over the JSON list and process it
 to generate either an HTML table or a HTML list, depending on what's inside.
 If suppose some key has array of objects and all the keys are same,
 instead of creating a new row for each such entry,
 club such values, thus it makes more sense and more readable table.

 @example:
 jsonObject = {
 "sampleData": [
 {"a":1, "b":2, "c":3},
 {"a":5, "b":6, "c":7}
 ]
 }
 OUTPUT:
 _____________________________
 | | | | |
 | | a | c | b |
 | sampleData |---|---|---|
 | | 1 | 3 | 2 |
 | | 5 | 7 | 6 |
 -----------------------------

 @contributed by: @muellermichel
 """
 if not list_input:
 return ""
 converted_output = ""
 column_headers = None
 if self.clubbing == 'simple':
 column_headers = self.column_headers_from_list_of_dicts(list_input)
 elif self.clubbing == 'forced':
 column_headers = self.forced_column_headers_from_list_of_dicts(list_input)
 if column_headers is not None:
 converted_output += self.table_init_markup
 converted_output += '<thead>'
 converted_output += '<tr><th>' + '</th><th>'.join(column_headers) + '</th></tr>'
 converted_output += '</thead>'
 converted_output += '<tbody>'
 for list_entry in list_input:
 converted_output += '<tr><td>'
 converted_output += '</td><td>'.join([self.convert_json_node(list_entry[column_header]) for column_header in
 column_headers if column_header in list_entry.keys()])
 converted_output += '</td></tr>'
 converted_output += '</tbody>'
 converted_output += '</table>'
 return converted_output
 #so you don't want or need clubbing eh? This makes @muellermichel very sad... ;(
 #alright, let's fall back to a basic list here...
 converted_output = '<ul><li>'
 converted_output += '</li><li>'.join([self.convert_json_node(child) for child in list_input])
 converted_output += '</li></ul>'
 return converted_output
 def convert_object(self, json_input):
 """
 Iterate over the JSON object and process it
 to generate the super awesome HTML Table format
 """
 if not json_input:
 return "" #avoid empty tables
 converted_output = self.table_init_markup + "<tr>"
 converted_output += "</tr><tr>".join([
 "<th>%s</th><td>%s</td>" %(
 self.convert_json_node(k),
 self.convert_json_node(v)
 )
 for k, v in json_input.items()
 ])
 converted_output += '</tr></table>'
 return converted_output
json2html = Json2Html()
if __name__ == "__main__":
 _json = [{'id': 126,
 'name': 'externalEnrollmentId',
 'order': 1,
 'isRequired': True,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 127,
 'name': 'externalClassSubjectId',
 'order': 2,
 'isRequired': True,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 128,
 'name': 'externalCurriculumId',
 'order': 3,
 'isRequired': True,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 129,
 'name': 'externalPeriodId',
 'order': 4,
 'isRequired': False,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 130,
 'name': 'startDate',
 'order': 5,
 'isRequired': False,
 'fieldType': 'date',
 'size': None,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 131,
 'name': 'endDate',
 'order': 6,
 'isRequired': False,
 'fieldType': 'date',
 'size': None,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 132,
 'name': 'externalEnrollmentClassSubjectStatusId',
 'order': 7,
 'isRequired': True,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'id': 133,
 'name': 'externalEnrollmentTypeId',
 'order': 8,
 'isRequired': False,
 'fieldType': 'alphanumeric',
 'size': 50,
 'importTypeId': 23,
 'description': 'Something goes in here',
 'values': []},
 {'name': 'ext.info.tags',
 'fieldType': 'array-alphanumeric',
 'size': 50,
 'isRequired': False,
 'order': 0,
 'values': [],
 'description': 'Something goes in here'}]
 display(HTML(json2html.convert(json = _json, clubbing='none')))
 display(HTML(json2html.convert(json = _json, clubbing='simple')))
 display(HTML(json2html.convert(json = _json, clubbing='forced')))

RESULTS

For the json shown in the sample above..

  • When json2html is called with the flag 'none':
    • It produces bulletpoints tables for each item of the list
  • When json2html is called with the flag 'simple':
    • It produces bulletpoints tables for each item of the list due to diffent length of keys in list of jsons
  • When json2html is called with the flag 'forced':
    • It produces a beautiful table in a perfect human readable format!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /