mirror of https://github.com/apache/jmeter.git
Bug 42582 - JSON pretty printing in Tree View Listener
git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/branches/rel-2-2@545917 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d32d1f709c
commit
e09e4965b0
|
|
@ -28,9 +28,12 @@ import java.awt.event.ActionListener;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.Character;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
|
|
@ -116,6 +119,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
private static final String HTML_BUTTON_LABEL = "Render HTML";
|
private static final String HTML_BUTTON_LABEL = "Render HTML";
|
||||||
|
|
||||||
|
private static final String JSON_BUTTON_LABEL = "Render JSON";
|
||||||
|
|
||||||
private static final String XML_BUTTON_LABEL = "Render XML";
|
private static final String XML_BUTTON_LABEL = "Render XML";
|
||||||
|
|
||||||
private static final String TEXT_BUTTON_LABEL = "Show Text";
|
private static final String TEXT_BUTTON_LABEL = "Show Text";
|
||||||
|
|
@ -124,6 +129,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
private static final String HTML_COMMAND = "html"; // $NON-NLS-1$
|
private static final String HTML_COMMAND = "html"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static final String JSON_COMMAND = "json"; // $NON-NLS-1$
|
||||||
|
|
||||||
private static final String XML_COMMAND = "xml"; // $NON-NLS-1$
|
private static final String XML_COMMAND = "xml"; // $NON-NLS-1$
|
||||||
|
|
||||||
private static final String TEXT_COMMAND = "text"; // $NON-NLS-1$
|
private static final String TEXT_COMMAND = "text"; // $NON-NLS-1$
|
||||||
|
|
@ -136,6 +143,19 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
private boolean textMode = true;
|
private boolean textMode = true;
|
||||||
|
|
||||||
|
private static final String ESC_CHAR_REGEX = "\\\\[\"\\\\/bfnrt]|\\\\u[0-9A-Fa-f]{4}"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static final String NORMAL_CHARACTER_REGEX = "[^\"\\\\]"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static final String STRING_REGEX = "\"(" + ESC_CHAR_REGEX + "|" + NORMAL_CHARACTER_REGEX + ")*\""; // $NON-NLS-1$
|
||||||
|
|
||||||
|
// This 'other value' regex is deliberately weak, even accepting an empty string, to be useful when reporting malformed data.
|
||||||
|
private static final String OTHER_VALUE_REGEX = "[^\\{\\[\\]\\}\\,]*"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static final String VALUE_OR_PAIR_REGEX = "((" + STRING_REGEX + "\\s*:)?\\s*(" + STRING_REGEX + "|" + OTHER_VALUE_REGEX + ")\\s*,?\\s*)"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static final Pattern VALUE_OR_PAIR_PATTERN = Pattern.compile(VALUE_OR_PAIR_REGEX);
|
||||||
|
|
||||||
// set default command to Text
|
// set default command to Text
|
||||||
private String command = TEXT_COMMAND;
|
private String command = TEXT_COMMAND;
|
||||||
|
|
||||||
|
|
@ -166,6 +186,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
private JRadioButton htmlButton;
|
private JRadioButton htmlButton;
|
||||||
|
|
||||||
|
private JRadioButton jsonButton;
|
||||||
|
|
||||||
private JRadioButton xmlButton;
|
private JRadioButton xmlButton;
|
||||||
|
|
||||||
private JCheckBox downloadAll;
|
private JCheckBox downloadAll;
|
||||||
|
|
@ -393,6 +415,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
showTextResponse(response);
|
showTextResponse(response);
|
||||||
} else if (command.equals(HTML_COMMAND)) {
|
} else if (command.equals(HTML_COMMAND)) {
|
||||||
showRenderedResponse(response, res);
|
showRenderedResponse(response, res);
|
||||||
|
} else if (command.equals(JSON_COMMAND)) {
|
||||||
|
showRenderJSONResponse(response);
|
||||||
} else if (command.equals(XML_COMMAND)) {
|
} else if (command.equals(XML_COMMAND)) {
|
||||||
showRenderXMLResponse(response);
|
showRenderXMLResponse(response);
|
||||||
}
|
}
|
||||||
|
|
@ -436,6 +460,7 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
resultsScrollPane.setViewportView(imageLabel);
|
resultsScrollPane.setViewportView(imageLabel);
|
||||||
textButton.setEnabled(false);
|
textButton.setEnabled(false);
|
||||||
htmlButton.setEnabled(false);
|
htmlButton.setEnabled(false);
|
||||||
|
jsonButton.setEnabled(false);
|
||||||
xmlButton.setEnabled(false);
|
xmlButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -447,6 +472,74 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
textButton.setEnabled(true);
|
textButton.setEnabled(true);
|
||||||
htmlButton.setEnabled(true);
|
htmlButton.setEnabled(true);
|
||||||
|
jsonButton.setEnabled(true);
|
||||||
|
xmlButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// It might be useful also to make this available in the 'Request' tab, for
|
||||||
|
// when posting JSON.
|
||||||
|
private static String prettyJSON(String json) {
|
||||||
|
StringBuffer pretty = new StringBuffer(json.length() * 2); // Educated guess
|
||||||
|
|
||||||
|
final String tab = ": "; // $NON-NLS-1$
|
||||||
|
StringBuffer index = new StringBuffer();
|
||||||
|
String nl = ""; // $NON-NLS-1$
|
||||||
|
|
||||||
|
Matcher valueOrPair = VALUE_OR_PAIR_PATTERN.matcher(json);
|
||||||
|
|
||||||
|
boolean misparse = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < json.length(); ) {
|
||||||
|
final char currentChar = json.charAt(i);
|
||||||
|
if ((currentChar == '{') || (currentChar == '[')) {
|
||||||
|
pretty.append(nl).append(index).append(currentChar);
|
||||||
|
i++;
|
||||||
|
index.append(tab);
|
||||||
|
misparse = false;
|
||||||
|
}
|
||||||
|
else if ((currentChar == '}') || (currentChar == ']')) {
|
||||||
|
if (index.length() > 0) {
|
||||||
|
index.delete(0, tab.length());
|
||||||
|
}
|
||||||
|
pretty.append(nl).append(index).append(currentChar);
|
||||||
|
i++;
|
||||||
|
int j = i;
|
||||||
|
while ((j < json.length()) && Character.isWhitespace(json.charAt(j))) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if ((j < json.length()) && (json.charAt(j) == ',')) {
|
||||||
|
pretty.append(","); // $NON-NLS-1$
|
||||||
|
i=j+1;
|
||||||
|
}
|
||||||
|
misparse = false;
|
||||||
|
}
|
||||||
|
else if (valueOrPair.find(i) && valueOrPair.group().length() > 0) {
|
||||||
|
pretty.append(nl).append(index).append(valueOrPair.group());
|
||||||
|
i=valueOrPair.end();
|
||||||
|
misparse = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!misparse) {
|
||||||
|
pretty.append(nl).append("- Parse failed from:");
|
||||||
|
}
|
||||||
|
pretty.append(currentChar);
|
||||||
|
i++;
|
||||||
|
misparse = true;
|
||||||
|
}
|
||||||
|
nl = "\n"; // $NON-NLS-1$
|
||||||
|
}
|
||||||
|
return pretty.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRenderJSONResponse(String response) {
|
||||||
|
results.setContentType("text/plain"); // $NON-NLS-1$
|
||||||
|
results.setText(response == null ? "" : prettyJSON(response));
|
||||||
|
results.setCaretPosition(0);
|
||||||
|
resultsScrollPane.setViewportView(results);
|
||||||
|
|
||||||
|
textButton.setEnabled(true);
|
||||||
|
htmlButton.setEnabled(true);
|
||||||
|
jsonButton.setEnabled(true);
|
||||||
xmlButton.setEnabled(true);
|
xmlButton.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,6 +602,7 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
resultsScrollPane.setViewportView(view);
|
resultsScrollPane.setViewportView(view);
|
||||||
textButton.setEnabled(true);
|
textButton.setEnabled(true);
|
||||||
htmlButton.setEnabled(true);
|
htmlButton.setEnabled(true);
|
||||||
|
jsonButton.setEnabled(true);
|
||||||
xmlButton.setEnabled(true);
|
xmlButton.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,7 +646,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
command = e.getActionCommand();
|
command = e.getActionCommand();
|
||||||
|
|
||||||
if (command != null
|
if (command != null
|
||||||
&& (command.equals(TEXT_COMMAND) || command.equals(HTML_COMMAND) || command.equals(XML_COMMAND))) {
|
&& (command.equals(TEXT_COMMAND) || command.equals(HTML_COMMAND)
|
||||||
|
|| command.equals(JSON_COMMAND) || command.equals(XML_COMMAND))) {
|
||||||
|
|
||||||
textMode = command.equals(TEXT_COMMAND);
|
textMode = command.equals(TEXT_COMMAND);
|
||||||
|
|
||||||
|
|
@ -570,6 +665,8 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
showTextResponse(response);
|
showTextResponse(response);
|
||||||
} else if (command.equals(HTML_COMMAND)) {
|
} else if (command.equals(HTML_COMMAND)) {
|
||||||
showRenderedResponse(response, res);
|
showRenderedResponse(response, res);
|
||||||
|
} else if (command.equals(JSON_COMMAND)) {
|
||||||
|
showRenderJSONResponse(response);
|
||||||
} else if (command.equals(XML_COMMAND)) {
|
} else if (command.equals(XML_COMMAND)) {
|
||||||
showRenderXMLResponse(response);
|
showRenderXMLResponse(response);
|
||||||
}
|
}
|
||||||
|
|
@ -627,6 +724,7 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
|
|
||||||
textButton.setEnabled(true);
|
textButton.setEnabled(true);
|
||||||
htmlButton.setEnabled(true);
|
htmlButton.setEnabled(true);
|
||||||
|
jsonButton.setEnabled(true);
|
||||||
xmlButton.setEnabled(true);
|
xmlButton.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -645,6 +743,12 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
htmlButton.setSelected(!textMode);
|
htmlButton.setSelected(!textMode);
|
||||||
group.add(htmlButton);
|
group.add(htmlButton);
|
||||||
|
|
||||||
|
jsonButton = new JRadioButton(JSON_BUTTON_LABEL);
|
||||||
|
jsonButton.setActionCommand(JSON_COMMAND);
|
||||||
|
jsonButton.addActionListener(this);
|
||||||
|
jsonButton.setSelected(!textMode);
|
||||||
|
group.add(jsonButton);
|
||||||
|
|
||||||
xmlButton = new JRadioButton(XML_BUTTON_LABEL);
|
xmlButton = new JRadioButton(XML_BUTTON_LABEL);
|
||||||
xmlButton.setActionCommand(XML_COMMAND);
|
xmlButton.setActionCommand(XML_COMMAND);
|
||||||
xmlButton.addActionListener(this);
|
xmlButton.addActionListener(this);
|
||||||
|
|
@ -657,6 +761,7 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
|
||||||
pane.add(textButton);
|
pane.add(textButton);
|
||||||
pane.add(htmlButton);
|
pane.add(htmlButton);
|
||||||
pane.add(xmlButton);
|
pane.add(xmlButton);
|
||||||
|
pane.add(jsonButton);
|
||||||
pane.add(downloadAll);
|
pane.add(downloadAll);
|
||||||
return pane;
|
return pane;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ Removed deprecated method JMeterUtils.split() - use JOrphanUtils version instead
|
||||||
<li>Bug 41913 - TransactionController now creates samples as sub-samples of the transaction</li>
|
<li>Bug 41913 - TransactionController now creates samples as sub-samples of the transaction</li>
|
||||||
<li>Bug 42506 - JMeter threads all use the same SSL session</li>
|
<li>Bug 42506 - JMeter threads all use the same SSL session</li>
|
||||||
<li>BeanShell elements now support ThreadListener and TestListener interfaces</li>
|
<li>BeanShell elements now support ThreadListener and TestListener interfaces</li>
|
||||||
|
<li>Bug 42582 - JSON pretty printing in Tree View Listener</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4>Non-functional improvements:</h4>
|
<h4>Non-functional improvements:</h4>
|
||||||
|
|
|
||||||
|
|
@ -1663,8 +1663,7 @@ this response, and some response codes.
|
||||||
Note that the Request panel only shows the headers added by JMeter.
|
Note that the Request panel only shows the headers added by JMeter.
|
||||||
It does not show any headers (such as Host) that may be added by the HTTP protocol implementation.
|
It does not show any headers (such as Host) that may be added by the HTTP protocol implementation.
|
||||||
<p>
|
<p>
|
||||||
There are three ways to view the response, selectable by a radio
|
There are several ways to view the response, selectable by a radio button.</p>
|
||||||
button.</p>
|
|
||||||
<p>The default view shows all of the text contained in the
|
<p>The default view shows all of the text contained in the
|
||||||
response.</p>
|
response.</p>
|
||||||
<p>The HTML view attempts to render the response as
|
<p>The HTML view attempts to render the response as
|
||||||
|
|
@ -1678,6 +1677,7 @@ If the checkbox is not selected, the renderer will not download images etc.
|
||||||
<p>The Render XML view will show response in tree style.
|
<p>The Render XML view will show response in tree style.
|
||||||
Any DTD nodes or Prolog nodes will not show up in tree; however, response may contain those nodes.
|
Any DTD nodes or Prolog nodes will not show up in tree; however, response may contain those nodes.
|
||||||
</p>
|
</p>
|
||||||
|
<p>The Render JSON view will show the response in tree style (also handles JSON embedded in JavaScript).</p>
|
||||||
</description>
|
</description>
|
||||||
<p>
|
<p>
|
||||||
The Control Panel (above) shows an example of an HTML display.
|
The Control Panel (above) shows an example of an HTML display.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue