How to parse JSON code block into object?

I use Gemini-2.0 Flash to return a json result but what I get is a JSON code block which starts with 3 backticks followed by json keyword in the response. When I try to use JSON.parse, it fails:

SyntaxError: Unexpected token '`', "```json
{
"... is not valid JSON

How can I do here to parse the JSON string into object, in Python and Javascript.

In javascript, I just use string.replace for now:

        let text = result.text.replace('```json', '')
        text = text.replace('```', '')

I’d strongly recommend you look at using Structured Output to avoid having to parse out markdown that may, or may not, be there in the response.

More details please.

1 Like

I don’t know if I do it correctly, I either get error with some some property have long string and others empty:

      const result = await this._genAI.models.generateContent({
        model: 'gemini-2.0-flash',
        contents: [
          createUserContent([
            prompt,
            imagePart,
          ]),
        ],
        config: {
          responseMimeType: "application/json",
          responseSchema: {
            type: Type.OBJECT,
            properties: {
              Date: {
                type: Type.STRING,
              },
              Currency: {
                type: Type.STRING,
              },
              Vendor: {
                type: Type.STRING,
              },
              Items: {
                type: Type.ARRAY,
                items: {
                  type: Type.OBJECT,
                  properties: {
                    Name: {
                      type: Type.STRING,
                    },
                    Amount: {
                      type: Type.NUMBER,
                    },
                  },
                  propertyOrdering: ["Name", "Amount"],
                },
              },
              TaxAmount: {
                type: Type.NUMBER,
              },
              Total: {
                type: Type.NUMBER
              }
            }
          },
        },
      });

My model:

export class ReceiptItemModel {
    public Name: string;
    public Amount: number;
}
export class ReceiptModel {
    public Date: string;
    public Currency: string; // (3-character currency code)
    public Vendor: string;
    public Items: Array<ReceiptItemModel>;
    public TaxAmount: number;
    public Total: number;
}

What I get:

extractReceiptDetails() receipt: {
  "Date": "09/09/2023",
  "Vendor": "Foot Locker Australia Inc\nΑ.Β.Ν. 22 619 093 977\nA.R.B.N. 089 717 251\nWESTFIELD SYDNEY\nSHOP 2043 PITT STREET MALL\nSYDNEY NW 2000\n0291891929\nVisit our website: www.footlocker.com.au\nStore: 2494837\nRegister: 4\nTime: 9:24\nTrans: 32049\nCashier: Fallon M.\nItem\nQty Price Amount\nTax%\n-NK AJ1 RET HI OG 'PALOMINO' BLK/GLD-YEL\n244103635504110\n1$250.00 $250.00\n10%\nSales Associate: Fallon M.\nSubtotal\n$250.00\nGST\n$22.73\nTotal\n$250.00\nMasterCard\n$250.00\nTransaction Type: Sale\nAuth Time: 9:24"
}
extractReceiptDetails() exception! SyntaxError: Unterminated string in JSON at position 40886 (line 3 column 40863)

It’s essential for your schema to have descriptions for each property, or the model has no way of knowing what to map to those fields. You can (and should) also then instruct it via the prompt. We do it via System Instructions, but that’s essentially just pre-context. Here’s an example from one of our projects:

const pageItemSchema = {
    type: Type.OBJECT,
    description: "Schema for a single page.", // Optional
    properties: {
        pageNumber: {
            type: Type.NUMBER,
            description: "The page number"
        },
        pageType: {
            type: Type.STRING,
            description: "The type of page",
            enum: [
                "cover", "image", "story", "gameplay", "characters",
                "locations", "weapons", "collectibles", "style"
            ]
        },
        pageMeta: {
            type: Type.STRING,
            description: "Additional non-displayed meta information about the page"
        },
        pageTitle: {
            type: Type.STRING,
            description: "The page title"
        },
        pageContent: {
            type: Type.STRING,
            description: "The full content for this page, in markdown format"
        }
    },
    required: ["pageNumber", "pageType", "pageMeta", "pageTitle", "pageContent"]
};

and here is a small snippet from our System Instructions:

    - Once they pick an idea, use your knowledge to create the game concept.
    - You will return your idea to the user as a series of 'pages' which go together to form a game design document.
    - Use the 'message' field of the responseSchema to ask the user to check over your idea.
    - Tell them they can ask you to change anything they don't like.
    - If the user says "Think of an idea for me - regarding page ..." go to step 8.

    - Use the 'pages' responseSchema array with the following:

        * pageNumber: The page number, starting at 1 and incrementing for each page.
        * pageType: The type of page. This must be exactly one of the following enums:
            - cover
            - image
            - story
            - gameplay
            - missions
            - characters
            - vehicles
            - locations
            - weapons
            - collectibles
            - style
        * pageMeta: Additional non-displayed meta information about the page.
        * pageTitle: The title of the page. If the pageType is 'cover' this should be the game title.
        * pageContent: The full content for this page in markdown format. Never repeat the pageTitle in the pageContent. Only add an h2 if it's different to the pageTitle.

You can instruct it what goes where, and how - and you should. As it cannot figure it out for itself. But it does mean you get fully formed output back again and don’t have to then parse it yourself.

Remember: What comes back will still always just be text. It will follow your structure, but you must do things like parseInt() for example to convert those types. The API doesn’t do that for you (at least not for TypeScript).

1 Like

If I still have to use parInt(), what’s the point of having a Type.NUMBER?

It’s a guide for the model and is used by Python. In TypeScript, you’re dealing with JSON though, which has no provision for the difference between an integer and a float, so it’s up to you to enforce it. Nothing removes the need for validation. Nor should it.

1 Like