9
example 
{
 "name" : "alan",
 "alias" : "nala"
}

If I use

morecomment=[s][\color{red}]{"}{"},

output

How can I highlight JSON string values (on the right side) but not the attributes (left side)?

  • Welcome to TeX.SX! Please help us to help you and add a minimal working example (MWE) that illustrates your problem. It will be much easier for us to reproduce your situation and find out what the issue is when we see compilable code, starting with \documentclass{...} and ending with \end{document}. – jub0bs Aug 08 '14 at 16:27
  • related: http://tex.stackexchange.com/questions/152829/how-can-i-highlight-yaml-code-in-a-pretty-way-with-listings and http://tex.stackexchange.com/questions/81646/how-can-i-highlight-text-before-a-character-with-listing – jub0bs Aug 08 '14 at 16:29
  • also related: http://tex.stackexchange.com/questions/81676/how-to-highlight-all-words-of-the-form-0-9a-za-z0-9-immediately-following-a – jub0bs Aug 08 '14 at 23:48

2 Answers2

13

A MWE is really needed here, so what follows is a bit of a shot in the dark...

Edit: now you can specify a distinct style for numerical JSON values.

enter image description here

\documentclass{article}

%\usepackage[T1]{fontenc}
%\usepackage[scaled=0.85]{beramono}
\usepackage{xcolor}
\usepackage{listings}

\newcommand\JSONnumbervaluestyle{\color{blue}}
\newcommand\JSONstringvaluestyle{\color{red}}

% switch used as state variable
\newif\ifcolonfoundonthisline

\makeatletter

\lstdefinestyle{json}
{
  showstringspaces    = false,
  keywords            = {false,true},
  alsoletter          = 0123456789.,
  morestring          = [s]{"}{"},
  stringstyle         = \ifcolonfoundonthisline\JSONstringvaluestyle\fi,
  MoreSelectCharTable =%
    \lst@DefSaveDef{`:}\colon@json{\processColon@json},
  basicstyle          = \ttfamily,
  keywordstyle        = \ttfamily\bfseries,
}

% flip the switch if a colon is found in Pmode
\newcommand\processColon@json{%
  \colon@json%
  \ifnum\lst@mode=\lst@Pmode%
    \global\colonfoundonthislinetrue%
  \fi
}

\lst@AddToHook{Output}{%
  \ifcolonfoundonthisline%
    \ifnum\lst@mode=\lst@Pmode%
      \def\lst@thestyle{\JSONnumbervaluestyle}%
    \fi
  \fi
  %override by keyword style if a keyword is detected!
  \lsthk@DetectKeywords% 
}

% reset the switch at the end of line
\lst@AddToHook{EOL}%
  {\global\colonfoundonthislinefalse}

\makeatother

\begin{document}

\section{A simple example}
\begin{lstlisting}[style=json]
example 
{
 "name" : "alan",
 "alias" : "nala",
}
\end{lstlisting}

\section{A more complicated example}
\begin{lstlisting}[style=json]
{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "height_cm": 167.6,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ]
}
\end{lstlisting}

\end{document}
jub0bs
  • 58,916
3

Building on @jub0bs's fantastic answer, I was able to devise the following language definition:

\documentclass{article}
%\usepackage[T1]{fontenc}
\usepackage{xcolor}
\usepackage{listings}

\newcommand\jsonkey{\color{purple}} \newcommand\jsonvalue{\color{cyan}} \newcommand\jsonnumber{\color{orange}}

% switch used as state variable \makeatletter \newif\ifisvalue@json

\lstdefinelanguage{json}{ tabsize = 4, showstringspaces = false, keywords = {false,true}, alsoletter = 0123456789., morestring = [s]{"}{"}, stringstyle = \jsonkey\ifisvalue@json\jsonvalue\fi, MoreSelectCharTable = \lst@DefSaveDef{:}\colon@json{\enterMode@json}, MoreSelectCharTable = \lst@DefSaveDef{,}\comma@json{\exitMode@json{\comma@json}}, MoreSelectCharTable = \lst@DefSaveDef{`{}\bracket@json{\exitMode@json{\bracket@json}}, basicstyle = \ttfamily }

% enter "value" mode after encountering a colon \newcommand\enterMode@json{% \colon@json% \ifnum\lst@mode=\lst@Pmode% \global\isvalue@jsontrue% \fi }

% leave "value" mode: either we hit a comma, or the value is a nested object \newcommand\exitMode@json[1]{#1\global\isvalue@jsonfalse}

\lst@AddToHook{Output}{% \ifisvalue@json% \ifnum\lst@mode=\lst@Pmode% \def\lst@thestyle{\jsonnumber}% \fi \fi %override by keyword style if a keyword is detected! \lsthk@DetectKeywords% }

\makeatother

\begin{document}

\section{A complex example} \begin{lstlisting}[language=json] { "hello": "world", "traits": {"rotated": false, "x": 100, "y": 200}, "name": "test" } \end{lstlisting}

\end{document}

The main fix introduced here is that whitespace is ignored --- multiple key/value pairs can exist on the same line and get highlighted correctly, while there can also be multiple lines between the key and the value and highlighting is also retained in this case:

enter image description here

Another useful improvement is that this version also supports highlighting of the key, rather than just the value. While this was not required for the initial question, it is a common use case, and since this question is at the top of searches on how highlight JSON in this manner, I feel that it is useful to include.

  • Great answer, thanks a lot! Also usable in beamer, when marking the frame fragile: \begin{frame}[fragile] ... \end{frame}. In that case the colors are maybe not the best, but thanks to your supplied commands, that is easily changed. – Eike Jun 23 '21 at 13:54
  • @Toastrackenigma This is amazing. Although it seems to cause weird behavior in listings placed in two horizontally aligned minipage environments. The style from this answer seems to also apply on the right-hand listing (if the JSON listing is placed on the left minipage environment). Do you have an idea as to what is the issue here? – CRoemheld Jun 24 '22 at 12:05