Skip to content

DJson Template Syntax

DJson is a powerful template engine that extends JSON with conditional logic and loops while maintaining valid JSON structure.

DJson Reference

For complete DJson documentation, syntax examples, and advanced features, visit the official DJson documentation at djson.dev

Overview

DJson allows you to: - Conditionally render sections with @djson if - Loop through collections with @djson for - Use variables with {{variable.name}} - Nest snippets with {{snippet.identifier}}

All while keeping your templates as valid JSON!

Conditional Rendering

Basic If Statement

Show a section only when a condition is met:

{
    "@type": "Product",
    "name": "{{product.name}}",
    "@djson if product.manufacturer": {
        "brand": {
            "@type": "Brand",
            "name": "{{product.manufacturer}}"
        }
    }
}

How it works: 1. DJson checks if product.manufacturer exists in the context 2. If it exists and is truthy, the brand section is included 3. If it doesn't exist or is null/empty, the entire brand section is removed

Output when manufacturer exists:

{
    "@type": "Product",
    "name": "Joust Duffle Bag",
    "brand": {
        "@type": "Brand",
        "name": "Nike"
    }
}

Output when manufacturer doesn't exist:

{
    "@type": "Product",
    "name": "Joust Duffle Bag"
}

Multiple Conditional Sections

You can have multiple conditional sections in the same object:

{
    "@type": "Product",
    "name": "{{product.name}}",
    "@djson if product.manufacturer": {
        "brand": {
            "@type": "Brand",
            "name": "{{product.manufacturer}}"
        }
    },
    "@djson if product.reviewCount": {
        "aggregateRating": {
            "@type": "AggregateRating",
            "ratingValue": "{{product.ratingValue}}",
            "reviewCount": "{{product.reviewCount}}"
        }
    }
}

Nested Conditionals

Conditionals can be nested:

{
    "@type": "Product",
    "@djson if product.reviewCount": {
        "aggregateRating": "{{snippet.aggregate-rating}}",
        "review": {
            "@djson for productReviews as review": {
                "@type": "Review",
                "author": "{{review.nickname}}"
            }
        }
    }
}

Loop Syntax

Basic Loop

Iterate through a collection:

{
    "@type": "ItemList",
    "itemListElement": {
        "@djson for products as product": {
            "@type": "ListItem",
            "position": "{{product.position}}",
            "name": "{{product.name}}"
        }
    }
}

Input Context:

[
    'products' => [
        ['position' => '1', 'name' => 'Product A'],
        ['position' => '2', 'name' => 'Product B'],
        ['position' => '3', 'name' => 'Product C']
    ]
]

Output:

{
    "@type": "ItemList",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": "1",
            "name": "Product A"
        },
        {
            "@type": "ListItem",
            "position": "2",
            "name": "Product B"
        },
        {
            "@type": "ListItem",
            "position": "3",
            "name": "Product C"
        }
    ]
}

Loop Variable Access

Inside a loop, use the loop variable as a prefix:

{
    "@djson for productReviews as review": {
        "@type": "Review",
        "author": "{{review.nickname}}",
        "reviewBody": "{{review.detail}}",
        "datePublished": "{{review.createdAt}}"
    }
}

Loop Variable Naming: - Collection: productReviews - Loop variable: review (singular) - Access fields: {{review.field_name}}

Nested Loops

Loops can be nested (though use sparingly for performance):

{
    "@djson for categories as category": {
        "@type": "Category",
        "name": "{{category.name}}",
        "products": {
            "@djson for category.products as product": {
                "@type": "Product",
                "name": "{{product.name}}"
            }
        }
    }
}

Empty Collections

If a collection is empty, the loop produces an empty array:

{
    "reviews": {
        "@djson for productReviews as review": {
            "@type": "Review"
        }
    }
}

Output when no reviews:

{
    "reviews": []
}

Combining Conditionals and Loops

Loop Inside Conditional

Show reviews only if they exist:

{
    "@type": "Product",
    "name": "{{product.name}}",
    "@djson if product.reviewCount": {
        "review": {
            "@djson for productReviews as review": {
                "@type": "Review",
                "author": {
                    "@type": "Person",
                    "name": "{{review.nickname}}"
                },
                "reviewBody": "{{review.detail}}",
                "datePublished": "{{review.createdAt}}"
            }
        }
    }
}

Benefits: - Entire review section is hidden when no reviews exist - Cleaner output without empty arrays - Better Schema.org compliance

Conditional Inside Loop

Apply conditions to each loop item:

{
    "@djson for products as product": {
        "@type": "Product",
        "name": "{{product.name}}",
        "@djson if product.specialPrice": {
            "offers": {
                "@type": "Offer",
                "price": "{{product.specialPrice}}"
            }
        }
    }
}

Real-World Examples

Example 1: Product with Optional Features

{
    "@context": "https://schema.org/",
    "@type": "Product",
    "name": "{{product.name}}",
    "description": "{{product.shortDescription}}",
    "sku": "{{product.sku}}",
    "image": "{{product.image}}",
    "@djson if product.manufacturer": {
        "brand": {
            "@type": "Brand",
            "name": "{{product.manufacturer}}"
        }
    },
    "offers": {
        "@type": "Offer",
        "price": "{{product.finalPrice}}",
        "priceCurrency": "{{store.currencyCode}}",
        "availability": "{{enum.availability.InStock}}"
    },
    "@djson if product.reviewCount": {
        "aggregateRating": {
            "@type": "AggregateRating",
            "ratingValue": "{{product.ratingValue}}",
            "reviewCount": "{{product.reviewCount}}",
            "bestRating": "5"
        },
        "review": {
            "@djson for productReviews as review": {
                "@type": "Review",
                "author": {
                    "@type": "Person",
                    "name": "{{review.nickname}}"
                },
                "reviewBody": "{{review.detail}}",
                "datePublished": "{{review.createdAt}}"
            }
        }
    }
}

What this does: 1. Always shows basic product info (name, SKU, image, offers) 2. Conditionally shows brand if manufacturer exists 3. Conditionally shows ratings and reviews if reviews exist 4. Loops through all reviews when they exist

Example 2: Breadcrumb Navigation

{
    "@context": "https://schema.org/",
    "@type": "BreadcrumbList",
    "itemListElement": {
        "@djson for breadcrumbs as breadcrumb": {
            "@type": "ListItem",
            "position": "{{breadcrumb.position}}",
            "name": "{{breadcrumb.label}}",
            "item": "{{breadcrumb.link}}"
        }
    }
}

Output:

{
    "@context": "https://schema.org/",
    "@type": "BreadcrumbList",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": "1",
            "name": "Home",
            "item": "https://example.com/"
        },
        {
            "@type": "ListItem",
            "position": "2",
            "name": "Gear",
            "item": "https://example.com/gear.html"
        },
        {
            "@type": "ListItem",
            "position": "3",
            "name": "Bags",
            "item": "https://example.com/gear/bags.html"
        }
    ]
}

Example 3: Category Product Listing

{
    "@context": "https://schema.org/",
    "@type": "ItemList",
    "itemListElement": {
        "@djson for products as product": {
            "@type": "ListItem",
            "position": "{{product.position}}",
            "url": "{{product.productUrl}}",
            "name": "{{product.name}}",
            "@djson if product.image": {
                "image": "{{product.image}}"
            }
        }
    }
}

Example 4: FAQ with Dynamic Questions

{
    "@context": "https://schema.org/",
    "@type": "FAQPage",
    "mainEntity": {
        "@djson for faqItems as faq": {
            "@type": "Question",
            "name": "{{faq.question}}",
            "acceptedAnswer": {
                "@type": "Answer",
                "text": "{{faq.answer}}"
            }
        }
    }
}

DJson Syntax Rules

Valid Conditional Placement

Correct:

{
    "@djson if condition": {
        "field": "value"
    }
}

Incorrect:

{
    "field": "@djson if condition"
}

Valid Loop Placement

Correct:

{
    "items": {
        "@djson for collection as item": {
            "name": "{{item.name}}"
        }
    }
}

Incorrect:

{
    "@djson for collection as item": "value"
}

Condition Evaluation

Conditions are truthy if: - Variable exists in context - Value is not null - Value is not empty string "" - Value is not false - Value is not 0

Conditions are falsy if: - Variable doesn't exist - Value is null, "", false, or 0

Performance Considerations

Keep Loops Shallow

Good:

{
    "@djson for productReviews as review": {
        "@type": "Review",
        "author": "{{review.nickname}}"
    }
}

Avoid:

{
    "@djson for categories as category": {
        "@djson for category.products as product": {
            "@djson for product.reviews as review": {
                "@type": "Review"
            }
        }
    }
}

Use Conditionals to Reduce Nesting

Good:

{
    "@djson if product.reviewCount": {
        "review": {
            "@djson for productReviews as review": {
                "@type": "Review"
            }
        }
    }
}

Better:

{
    "@djson if product.reviewCount": {
        "aggregateRating": "{{snippet.aggregate-rating}}",
        "review": "{{snippet.review-list}}"
    }
}

Debugging DJson

Enable Pretty Print

  1. Navigate to: StoresConfigurationQoliberSEO: Rich Snippets
  2. Set Debug Mode to pretty
  3. View page source to see formatted JSON

Check Logs

Debug information is logged to var/log/debug.log:

[2024-11-18 10:00:00] main.DEBUG: Final context for DJson {"snippet_name":"product","has_productReviews":true,"review_count":3}

Common Issues

Issue: Loop not producing output

Solution: Check that: 1. Collection name is correct 2. Collection is in context 3. Collection is not empty 4. Loop variable syntax is correct

Issue: Conditional always false

Solution: Check that: 1. Variable exists in context 2. Variable is not null or empty 3. Condition syntax is correct: @djson if variable.name

Advanced DJson Patterns

Pattern: Fallback Values

Use nested snippets for complex fallbacks:

{
    "@djson if product.specialPrice": {
        "price": "{{product.specialPrice}}"
    },
    "@djson if !product.specialPrice": {
        "price": "{{product.price}}"
    }
}

Note: DJson doesn't support ! negation directly. Use separate snippets instead.

Pattern: Reusable Components

Create small, reusable snippet components:

Component: product-offer snippet

{
    "@type": "Offer",
    "price": "{{product.finalPrice}}",
    "priceCurrency": "{{store.currencyCode}}"
}

Usage:

{
    "@type": "Product",
    "offers": "{{snippet.product-offer}}"
}

Pattern: Conditional Snippets

Combine conditionals with snippets:

{
    "@djson if product.reviewCount": {
        "review": "{{snippet.review-list}}"
    }
}

Next Steps