Open Product Feed Format (OPFF), wersja 0.9

Komentarze, uwagi i sugestie proszę wysyłać do: jan@rychter.com

OPFF jest formatem wymiany danych E-commerce. Umożliwia odejście od archaicznych, niestandardowych i wzajemnie niekompatybilnych plików XML. Zaprojektowany jest tak, by być formatem elastycznym i rozszerzalnym, nadającym się do wielu zastosowań. Jest też otwarty: dostępna jest pełna dokumentacja, a wkrótce pojawią się również narzędzia sprawdzające poprawność.

OPFF został zaprojektowany w Fablo po latach doświadczeń z setkami plików XML z danymi, uwzględniając potrzeby kilkunastu różnych projektów. Staramy się narzucać jak najmniej, pozostawiając dużą elastyczność. Chcemy rozwiązać podstawowy problem: jak przesyłać dane o produktach przez sieć.

Co to znaczy “otwarty”?

Przez “otwarty format” rozumiemy to, że jest w pełni udokumentowany, specyfikacja jest dostępna dla każdego, zaś sam format jest łatwo rozszerzalny, pozostając równocześnie ustandaryzowanym i kompatybilnym. Specjalnie zaprojektowane pola “extra-info” przy każdym elemencie pozwalają na dowolne rozszerzanie o dane potrzebne przy konkretnych wdrożeniach, bez potrzeby naruszania kompatybilności.

Chcemy, by format pozostał pod kontrolą Fablo: będziemy uaktualniać dokumentację i przygotowywać kolejne wersje. Taka kontrola jest ważna, by uniknąć fragmentacji: powstania kilku wzajemnie niekompatybilnych wersji.

Chcemy słyszeć o ewentualnych ograniczeniach i problemach, oraz o sugestiach rozszerzeń. Zamierzamy projektować kolejne wersje formatu wraz z opcjonalnymi rozszerzeniami, tak aby służył on w jak największej liczbie zastosowań.

Planujemy opublikowanie narzędzi testujących zgodność plików ze specyfikacją formatu.

Przykład

Oto minimalny przykład pliku OPFF:

{
    "metadata": { "version": "0.9" }, 
    "products": [
        {
            "id": "699218791", 
            "name": "Czekoladki: Zestaw Specjalny", 
            "categories": [ "Prezenty>>Czekoladki" ], 
            "description": "Specjalny zestaw czekoladek", 
            "images": [ "http://fablo.pl/images/0206-PLXXXX.jpg" ], 
            "attributes": { "Kolor opakowania": "Czarny" },
            "price": 93.0, 
            "url": "http://example.url.com/product",
            "extra-info": { "Waga": "0,25" }
        }
    ]
}

Po co nowy format?

Obecnie używane na rynku formaty wymiany danych mają mnóstwo wad. Są nieudokumentowane i nie posiadają precyzyjnej specyfikacji. Istnieją w kilku wersjach, których nie ma jak rozróżnić. Bazują na XML, przez co pliki z danymi są niepotrzebnie duże. Istnieje dowolność w formatowaniu liczb: ceny są przekazywane jako “2,99”, “2.99” lub “2.99zł”. Kodowanie polskich znaków nie jest ustandaryzowane. Mnóstwo czasu i wysiłku programistów marnowane jest na przetwarzanie formatów i obsługę źle przygotowanych plików.

Najgorszym jednak problemem jest to, że istniejące struktury XML nie wspierają nowoczesnych sklepów. Niewiele struktur pozwala na przypisywanie produktów do kilku kategorii naraz. Żaden z obecnie używanych formatów nie obsługuje w pełni wariantów produktów. Żaden też nie umożliwia przesłania pełnych danych o produktach (z wariantami) od wielu sprzedawców. Formaty nie nadają się też do inkrementalnej aktualizacji.

Postanowiliśmy temu zaradzić i na podstawie dwóch lat doświadczeń z setkami baz danych naszych klientów opracowaliśmy nowy format, który obejmuje wszystkie znane nam przypadki użycia.

Format zaprojektowany jest tak, by nie trzeba było używać wszystkich jego możliwości, jeśli nie są potrzebne. Dlatego podstawowe pliki z danymi nie są skomplikowane. Jest też elastyczny i narzuca minimalną ilość ograniczeń. Spodziewamy się, że w praktyce użytkownicy formatu narzucać będą dodatkowe ograniczenia, takie jak wymagany zestaw atrybutów w pewnych kategoriach.

Zależało nam również na rozszerzalności. Dlatego format zdefiniowany jest tak, że wszędzie jest miejsce na dodanie dowolnych dodatkowych informacji, jakie mogą być potrzebne w konkretnych aplikacjach. Są to sekcje “extra-info” w danych produktów, wariantów produktów, danych sprzedawców i metadanych całego pliku. Dzięki temu możliwe jest użycie jednego formatu bazowego, zamiast definiować oddzielny format dla każdego zastosowania, rynku, czy kraju (jak robi to np. Google).

JSON

JSON (JavaScript Object Notation) jest eleganckim formatem serializacji danych. Jest doskonale opisany i ustandaryzowany (RFC4627), istnieje mnóstwo bibliotek do różnych języków, które potrafią go generować. Łatwo jest też sprawdzić poprawność składniową: każdy plik JSON musi być poprawnym kodem JavaScript.

Dużą zaletą JSON w porównaniu do XML jest rozmiar i szybkość przetwarzania: na rzeczywistych danych obserwujemy oszczędności rzędu 30–50%.

Do generowania JSON polecamy użycie jednej z gotowych bibliotek, takich jak:

UWAGA: nie należy JSON próbować generować “ręcznie” za pomocą print/echo. Takie pliki niemal zawsze będą błędne.

Obszerny spis bibliotek i narzędzi do JSON można znaleźć pod http://json.org/.

Inne reprezentacje, w tym binarne

W przyszłości przewidujemy zdefiniowanie innych metod kodowania informacji OPFF, przy zachowaniu struktury. Mogą to być takie formaty jak BSON lub Protocol Buffers.

Pojęcia

Produkty

Produkt jest podstawowym elementem w danych. Produkt może istnieć albo samodzielnie, albo w postaci kombinacji produktu głównego i jego wariantów.

Identyfikator produktu lub wariantu (id)

Dowolny ciąg znaków, umożliwiający identyfikację produktu lub jego konkretnego wariantu i złożenie zamówienia. Identyfikatory w formacie OPFF celowo są ciągami znaków, a nie liczbami, by można było bezpośrednio wykorzystać istniejące już kody (SKU), bez wprowadzania dodatkowych bytów wyłącznie do przesyłania danych.

Atrybuty produktu (attributes)

Atrybuty produktu to takie jego parametry, które są używane do filtrowania i wyszukiwania. Przestrzeń atrybutów jest kontrolowana przez dostarczającego dane i jest dowolnie rozszerzalna.

Nie należy mylić atrybutów (“attributes”) z informacjami dodatkowymi (“extra-info”). Atrybuty są przeznaczone do filtrowania, nie należy więc wstawiać tam np. informacji o wadze produktu lub cenie dostawy. Wszystkie informacje, po których nie ma sensu filtrować listy produktów powinny trafiać do informacji dodatkowych (“extra-info”).

Atrybuty mogą być wielowartościowe: w takim przypadku zamiast pojedyńczej wartości może pojawić się lista wartości.

W OPFF celowo nie są zdefiniowane wymagane atrybuty dla poszczególnych kategorii produktów.

Informacje dodatkowe (extra-info)

Wiele elementów formatu ma zdefiniowane pole “extra-info”. Służy ono do przekazywania dowolnych informacji nie określonych w standardzie OPFF, a potrzebnych w konkretnym zastosowaniu. Znaczenie pól “extra-info” jest ustalane między wysyłającym a odbierającym dane, OPFF narzuca jedynie, że są to mapy (obiekty), gdzie klucze są łańcuchami znakowymi, a wartości mogą być albo pojedyńczym łańcuchem znakowym, albo listą łańcuchów znakowych.

Kategorie

Zarówno produkty jak i dostawcy mogą mieć przypisane kategorie. Kategorie są reprezentowane jako łańcuchy znakowe, przy czym możliwe jest przypisanie kilku kategorii do jednego produktu. W nazwie kategorii separatorem członów są dwa znaki >> — umożliwia to reprezentację hierarchii kategorii.

UWAGA: celowo nie wybrano znaku ‘/’ (ukośnik) jako separatora członów nazwy kategorii. Ten znak bardzo często występuje w nazwie samej kategorii (np. “DVD/Blu-ray”) i używanie go jako separatora powoduje błędy lub niejasności.

Ceny

Ceny reprezentowane są jako liczby zmiennoprzecinkowe. Z założenia wszystkie ceny są w jednej walucie, którą można opcjonalnie wyspecyfikować w sekcji “metadata” (pole “currency”) jako trzyznakowy kod waluty.

UWAGA: ceny są liczbami w JSON, a nie łańcuchami znakowymi. Plik zawierający ceny jako łańcuchy znakowe (“2.99”) nie jest poprawny.

Warianty produktu (variants)

Wariantem produktu nazywamy jego pojedynczą odmianę, albo wersję. Przykładowo, w branży odzieżowej bluzka może być dostępna w sześciu kolorach, każdy z nich w sześciu wielkościach. Razem daje to 36 wariantów. Każdy z nich posiada swój identyfikator, umożliwiający realizację zamówienia na ten konkretny wariant.

Dzięki polom “options” możliwe jest różnicowanie i filtrowanie wariantów. Można mieć dowolną ilość pól “options”, ale wszystkie warianty produktu muszą mieć taki sam ich zestaw. W praktyce rzadko spotyka się więcej niż dwie opcje.

Opcje wariantów nie są wielowartościowe: jedna opcja dla jednego wariantu zawsze ma tylko jedną wartość.

Warianty nie posiadają atrybutów (tę rolę spełniają “options”), możliwe jest za to podawanie dodatkowych informacji dla każdego wariantu (“extra-info”).

Dostawcy (vendors)

W przypadku danych porównujących oferty wielu dostawców przydatne staje się pole vendor w wariantach produktu. Wariant produktu staje się wtedy ofertą danego dostawcy na dany produkt. Określona jest cena, konkretny wariant (poprzez options), oraz dostawca poprzez pole vendor.

Pole vendor jest identyfikatorem dostawcy. Pełna lista dostawców może (choć nie musi) być przekazana oddzielnie w obiekcie vendors.

Dla produktów posiadających kilka wariantów (różniących się np. kolorem lub rozmiarem) oferty są rozumiane jako oferta na konkretny wariant. Każda z kombinacji kolor/rozmiar może więc mieć inną grupę dostawców.

Elementy strumienia danych

Plik lub strumień z danymi musi być poprawnym JSON (RFC4627).

Dodatkowo nakłada się ograniczenia:

Strumień danych zawierać może dane nie tylko o produktach. Dlatego podzielony jest na części:

Metadane (metadata)

Część metadanych pozwala na przekazanie metainformacji o tym, co jest zawarte w pliku. W sekcji tej wszystkie pola poza “version” są opcjonalne. Zdefiniowane są następujące pola:

Pole “version” dla obecnej wersji formatu powinno mieć wartość “0.9”.

Pole “extra-info” może zawierać obiekt (mapę) klucz-wartość, gdzie można wstawić dowolne rozszerzenia formatu o dodatkowe pola metadanych. Nie należy dodawać nowych pól poza “extra-info”.

Przykładowa sekcja “metadata”:

"metadata": {
    "version": "0.9",
    "created-by": "Fablo generator",
    "created-date": "2011-10-31T13:25:13+01:00",
    "currency": "PLN",
    "language": "pl"
}

Minimalna obowiązkowa sekcja “metadata”:

"metadata": {
    "version": "0.9",
}

Produkty (products)

Każdy produkt reprezentowany jest jako obiekt (mapa). Jedynym obowiązkowym polem jest “id”. Obecność pozostałych zależy od aplikacji.

Przykładowy produkt w postaci bezwariantowej:

    {
        "id": "699218791", 
        "name": "Czekoladki: Zestaw Specjalny", 
        "categories": ["Prezenty>>Czekoladki"], 
        "description": "Specjalny zestaw czekoladek", 
        "images": ["http://fablo.pl/images/0206-PLXXXX.jpg"],
        "price": 93.0,
        "url": "http://example.url.com/product",
        "extra-info": { "Waga": "0,25" }
    }, 

Część “extra-info” jest luźno specyfikowana: jest to mapa klucz-wartość, gdzie klucze to łańcuchy znakowe (string), a wartości to łańcuchy znakowe lub listy łańcuchów znakowych. Można tam przechowywać dowolne dodatkowe informacje o produkcie. Nie należy dodawać nowych pól poza “extra-info”.

Warianty produktu (variants)

Każdy produkt może istnieć w wielu wariantach, zaś na każdy wariant może istnieć wiele ofert sprzedawców. Dla sukienki oferowanej w czterech rozmiarach i czterech kolorach przez trzech sprzedawców, z których każdy ma pełen asortyment otrzymujemy więc 4 x 4 x 3, czyli 48 wariantów.

Warianty są reprezentowane jako element (lista) o nazwie “variants” w danych produktu. Lista ta zawiera obiekty (mapy), z których każda przedstawia jeden z wariantów. Warianty są opcjonalne, przy czym w przypadku gdy są podawane, jedynym obowiązkowym polem jest “id”.

Opcje wariantowe definiowane są w części “options”. Ich znaczenie jest analogiczne do “attributes” w opisie produktu — z reguły używane są do konstrukcji filtrów. W wariantach jednak celowo nazwane są inaczej: aby dane były poprawne, wszystkie warianty produktu muszą mieć taki sam zestaw opcji wariantowych. Opcje wariantowe wyznaczają możliwości filtrowania i wszystkie warianty muszą się co do nich zgodzić. Nie jest więc możliwe, by jeden wariant definiował opcje “Rozmiar” i “Kolor”, a drugi tylko “Rozmiar”.

Opcje wariantowe muszą mieć inne nazwy niż atrybuty produktu.

Przykładowa lista zawierająca jeden wariant:

        "variants": [
            {
                "id": "699218751-1", 
                "price": 199.51, 
                "url": "http://example.url.com/product", 
                "images": ["http://fablo.pl/images/0204-PLXXXX.jpg", "http://fablo.pl/images/0204-PLXXXX.jpg"],
                "vendor": "98717",
                "options": {
                    "Kolor": "czarny",
                    "Czekoladek": "21"
                },
                "extra-info": { "points": "598" }
            }]

Pola dostępne w wariancie produktu:

Nie należy dodawać nowych pól poza “extra-info”.

Sprzedawcy (vendors)

Opcjonalna informacja o sprzedawcach przydatna jest w przypadku współpracy z porównywarkami cenowymi, gdzie występuje wielu oferentów. Przykładowy opis sprzedawcy:

"vendors": [
    {
        "id": "98717", 
        "name": "Chocolissimo",
        "categories": [
            "Delikatesy", 
            "Prezenty, gad\u017cety"
        ], 
        "description": "Chocolatte to sklep sprzedaj\u0105cy r\u0119cznie robione czekoladki.", 
        "images": ["http://fablo.pl/vendor-logo.png"]
    }]

Dostępne pola:

Kategorie sprzedawców mogą być niezależne od kategorii produktów, używają za to tego samego hierarchicznego formatu.

Nie należy dodawać nowych pól poza “extra-info”.

Format OPFF opisany w Protocol Buffers

Google Protocol Buffers to wydajny binarny format serializacji danych z precyzyjnym językiem specyfikacji. Załączamy tu definicję, jakiej sami używamy do przetwarzania danych, może okazać się pomocna:

import "clojure/protobuf/extensions.proto";

message Attribute {
  required string key = 1;
  repeated string val = 2 [(set) = true];
};

message Metadata {
  required string version = 1;
  optional string created_by = 2;
  optional string created_date = 3;
  optional string currency = 4;
  optional string language = 5;

  repeated Attribute extra_info = 100 [(map) = true];
}

message Product {
  required string id = 1;

  optional string name = 2;
  optional string description = 3;
  optional string keywords = 4;
  repeated string categories = 5;

  optional float price = 6;
  optional string url = 7;
  repeated string images = 8;

  repeated Attribute attributes = 100 [(map) = true];
  repeated Attribute extra_info = 200 [(map) = true];

  repeated Variant variants = 120;

  message Variant {
    required string id = 1;

    optional float price = 6;
    optional string url = 7;
    repeated string images = 8;

    optional string vendor = 5;

    message Option {
      required string key = 1;
      required string val = 2;
    };

    repeated Option options = 300 [(map) = true];
    repeated Attribute extra_info = 400 [(map) = true];
  };
}

message Vendor {
  required string id = 1;
  optional string name = 2;
  optional string description = 3;
  optional string keywords = 4;
  repeated string images = 5;
  repeated string categories = 6;
  optional string url = 7;

  repeated Attribute extra_info = 100 [(map) = true];
};

W przyszłości przewidujemy rozszerzenie formatu OPFF o reprezentację binarną opartą o Protocol Buffers. Struktury danych i wszystkie warunki poprawności zostaną zachowane, dodany jedynie zostanie nowy format serializacji. Postać binarna przydatna jest w dużych wdrożeniach, gdzie przesyłane są większe ilości danych.

Rozszerzanie formatu

Zależy nam na tym, by format był rozszerzalny i by pozostał standardem. Nie chcemy, by pojawiło się kilka niekompatybilnych ze sobą wariantów. Dlatego format zdefiniowany jest tak, że wszędzie jest miejsce na dodanie dowolnych dodatkowych informacji, jakie mogą być potrzebne w konkretnych aplikacjach. Są to sekcje “extra-info” w danych produktów, wariantów produktów i sprzedawców.

Nie należy dodawać dodatkowych pól poza sekcjami “extra-info”. W przypadku gdyby format okazał się niewystarczający, prosimy o kontakt, być może warto go rozszerzyć — ale warto zrobić to w sposób przemyślany.

Przykłady użycia

Przekazywanie danych sklepu do porównywarki cenowej lub wyszukiwarki

Dla nieskomplikowanych sklepów wystarczy podać dane produktów bez wariantów. Ta postać danych najbliżej odpowiada istniejącym na rynku plikom XML.

W danych nie pojawią się informacje o wariantach ani sprzedawcach.

Przekazywanie wariantowych danych sklepu do porównywarki cenowej lub wyszukiwarki

W danych pojawią się produkty wraz z wariantami, ale ceny nadal będą ustawione w produktach, a nie w poszczególnych wariantach, bo z reguły wszystkie warianty danego produktu będą w tej samej cenie. Warianty zawierać będą informacje o opcjach wariantowych (rozmiar, kolor itp.) oraz ewentualnie zdjęcia specyficzne dla danego wariantu.

W danych nie pojawią się informacje o sprzedawcach.

Przekazywanie wariantowych danych z ofertami

Ten przypadek ma miejsce w przypadku przesyłania danych np. między siecią/agencją reklamową a porównywarką cenową. Dane zawierają informacje o produktach, wariantach, ofertach cenowych na dane warianty, oraz dostawcach.

Dostawcy składają oferty na konkretne warianty produktu (czyli np. kolor/rozmiar danej sukienki w pewnej cenie). W takim przypadku ceny definiowane są w wariantach, z których każdy jest ofertą. Nie definiuje się wtedy ceny samego produktu.

Przykłady plików z danymi

Minimalny

{
    "metadata": {
        "version": "0.9"
    }, 
    "products": [
        {
            "id": "699218791", 
            "name": "Czekoladki: Zestaw Specjalny", 
            "categories": [ "Prezenty>>Czekoladki" ], 
            "description": "Specjalny zestaw czekoladek", 
            "images": [ "http://fablo.pl/images/0206-PLXXXX.jpg" ], 
            "attributes": { "Kolor opakowania": "Czarny" },
            "price": 93.0, 
            "url": "http://example.url.com/product",
            "extra-info": { "Waga": "0,25" }
        }
    ]
}

Z wariantami i sprzedawcami

{
    "metadata": {
        "version": "0.9"
    }, 
    "products": [
        {
            "categories": [
                "Prezenty>>Czekoladki"
            ], 
            "description": "Ekskluzywny podarunek w oryginalnej drewnianej skrzyneczce.", 
            "id": "699218751", 
            "images": [
                "http://fablo.pl/images/0204-PLXXXX.jpg"
            ], 
            "name": "Czekoladki: ChocoLoco", 
            "variants": [
                {
                    "extra-info": {
                        "points": "598"
                    }, 
                    "id": "699218751-1", 
                    "images": [
                        "http://fablo.pl/images/0204-PLXXXX.jpg", 
                        "http://fablo.pl/images/0204-PLXXXX.jpg"
                    ], 
                    "options": {
                        "Czekoladek": "21", 
                        "Kolor": "czarny"
                    }, 
                    "price": 199.51, 
                    "url": "http://example.url.com/product", 
                    "vendor": "98717"
                }, 
                {
                    "extra-info": {
                        "points": "598"
                    }, 
                    "id": "699218751-2", 
                    "images": [
                        "http://fablo.pl/images/0204-PLXXXX.jpg", 
                        "http://fablo.pl/images/0204-PLXXXX.jpg"
                    ], 
                    "options": {
                        "Czekoladek": "21", 
                        "Kolor": "czerwony"
                    }, 
                    "price": 189.51, 
                    "url": "http://example.url.com/product", 
                    "vendor": "98717"
                }, 
                {
                    "extra-info": {
                        "points": "598"
                    }, 
                    "id": "699218751-3", 
                    "images": [
                        "http://fablo.pl/images/0204-PLXXXX.jpg", 
                        "http://fablo.pl/images/0204-PLXXXX.jpg"
                    ], 
                    "options": {
                        "Czekoladek": "33", 
                        "Kolor": "czarny"
                    }, 
                    "price": 231.99, 
                    "url": "http://example.url.com/product", 
                    "vendor": "98717"
                }, 
                {
                    "extra-info": {
                        "points": "598"
                    }, 
                    "id": "699218751-4", 
                    "images": [
                        "http://fablo.pl/images/0204-PLXXXX.jpg", 
                        "http://fablo.pl/images/0204-PLXXXX.jpg"
                    ], 
                    "options": {
                        "Czekoladek": "33", 
                        "Kolor": "czerwony"
                    }, 
                    "price": 233.2, 
                    "url": "http://example.url.com/product", 
                    "vendor": "98717"
                }
            ]
        }
    ], 
    "vendors": [
        {
            "categories": [
                "Delikatesy", 
                "Prezenty, gad\u017cety"
            ], 
            "description": "Chocolatte to sklep sprzedaj\u0105cy r\u0119cznie robione czekoladki.", 
            "id": "98717", 
            "images": [
                "http://fablo.pl/vendor-logo.png"
            ], 
            "name": "Chocolatte"
        }
    ]
}