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)?

3

2 Answers 2

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}
4
  • how about set another style number w/o quote? Aug 9, 2014 at 15:28
  • @user3782003 See my edit. In the future, please post a minimal working example (MWE) in your question.
    – jub0bs
    Aug 9, 2014 at 16:04
  • What about having escaped quotes on the right side? I have got this one: "content": "<Dat url=\"yawRate\"\\n ><Abs name=\"yawRate\" val=\"-.01\"/>\\n</Dat>" and want the whole string to be in one color as it is one value. Can you help?
    – tellob
    Jun 18, 2015 at 6:52
  • Did with an additional morestring = [s]{"<}{>"}
    – tellob
    Jun 18, 2015 at 7:08
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.

2
  • 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, 2021 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, 2022 at 12:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .