WURFL Parsing
WURFL (Wireless Universal Resource FiLe) is used by MediaMath to understand information about devices, browser, OS’s, and their capabilities. The following section discusses how to work with the various features that contain WURFL data.
1. Combine targeted and untargeted fields from the contextual data
• Combine targeted and untargeted array to one array.
• Sort resulting array by string length and pick the largest string for each dim.
{"24":{"1":{"targeted":["br_Firefox"],"untargeted":["br_Chrome Mobile:ve_67.0.3396"]}}}
targeted_and_untargeted = ["br_Chrome Mobile:ve_67.0.3396", "br_Firefox"]
-> for dimension "24" we will pick the largest string as follows "br_Chrome Mobile:ve_67.0.3396"
entire output:
browser="br_ChromeMobile"
browser_version="br_ChromeMobile:ve_67.0.3396"
os=""
os_version=""
device_type=""
device_manufacturer=""
device_model=""
2. WURFL features extraction
All features have default values if empty string (""). It will always generate 7 features as below:
2.1 browser
Sets the name of the browser. It looks for identifier br_
and reads the browser name until the first ":" char. It will fallback to an empty string if there are no more chars after br_
.
2.2 browser_version
Sets the version of the browser. It looks for the identifier br_
and ve_
and reads the entire string. It will fallback to an empty string if there are no more chars after ve_
.
2.3 os
Sets the name of the operating system. It looks for the identifier os_
and reads the os name until the first ":" char. It will fallback to an empty string if there are no more chars afteros_
.
2.4 os_version
Sets the version of the operating system. It looks for the identifier os_
and ve_
and reads the entire string. It will fallback to an empty string if there are no more chars after ve_
.
2.5 device_manufacturer
Sets the manufacturer.It looks for the identifier ma_
and reads the os name until the first ":" char. It will fallback to an empty string if there are no more chars after ma_
.
2.6 device_model
Sets the manufacturer and model. It looks for the identifier ma_
and mo_
and reads the entire string. It will fallback to an empty string if there are no more chars after mo_
.
2.7 device_type
Sets the type of the device and may return any of these values: Desktop, App, Tablet, Smartphone, Feature Phone, Smart-TV, Robot, Other non-Mobile, Other Mobile.It look for the identifier fo_
and reads the entire string. It will fallback to an empty string if there are no more chars after fo_
.
3. Example Feature extraction
3.1 Feature extraction Example 1
{"24":{"1":{"targeted":[],"untargeted":["br_Chrome Mobile:ve_67.0.3396"]}},"25":{"1":{"targeted":[],"untargeted":["os_Android:ve_8.1.0"]}},"29":{"1":{"targeted":[],"untargeted":["ma_Generic:mo_Android 2.0"]}},"26":{"1":{"targeted":[],"untargeted":["fo_Feature Phone"]}}}
browser="br_ChromeMobile"
browser_version="br_ChromeMobile:ve_67.0.3396"
os="os_Android"
os_version="os_Android:ve_8.1.0"
device_type="fo_FeaturePhone"
device_manufacturer="ma_Generic"
device_model="ma_Generic:mo_Android2.0"
3.2 Feature extraction Example 2
{"24":{"1":{"targeted":[],"untargeted":["br_"]}},"25":{"1":{"targeted":[],"untargeted":["os_Android:ve_"]}},"29":{"1":{"targeted":[],"untargeted":["ma_:mo_"]}},"26":{"1":{"targeted":[],"untargeted":["fo_Feature Phone"]}}}
browser=""
browser_version=""
os="os_Android"
os_version=""
device_type="fo_FeaturePhone"
device_manufacturer=""
device_model=""
4.Source Code
package main
import (
"fmt"
"sort"
"strings"
"encoding/json"
)
type Num1 struct {
Targeted []string `json:"targeted"`
Untargeted []string `json:"untargeted"`
}
type wurfulData struct {
Num24 struct {
Dim Num1 `json:"1"`
} `json:"24"`
Num25 struct {
Dim Num1 `json:"1"`
} `json:"25"`
Num26 struct {
Dim Num1 `json:"1"`
} `json:"26"`
Num29 struct {
Dim Num1 `json:"1"`
} `json:"29"`
}
func extractDim(dim *Num1, dimID string, appendTo *[]string) {
type conf struct {
lookup []string
logbrainNames []string
}
var dimLookUpMap = map[string]conf{
"24": conf{[]string{"br_", "ve_"}, []string{"browser", "browser_version"}},
"25": conf{[]string{"os_", "ve_"}, []string{"os", "os_version"}},
"29": conf{[]string{"ma_", "mo_"}, []string{"device_manufacturer", "device_model"}},
"26": conf{[]string{"fo_"}, []string{"device_type"}},
}
dim.Targeted = append(dim.Targeted, dim.Untargeted...)
sort.Slice(dim.Targeted, func(i, j int) bool {
return len(dim.Targeted[i]) > len(dim.Targeted[j])
})
cf := dimLookUpMap[dimID]
wurflFeatures := make(map[string]string)
for _, featureName := range cf.logbrainNames {
wurflFeatures[featureName] = ""
}
// at the moment we only support dim_id = 24,25,26,29
if len(cf.lookup) <= 0 {
return
}
if len(dim.Targeted) <= 0 {
for featureName, featureVal := range wurflFeatures {
*appendTo = append(*appendTo,featureName+"^"+featureVal)
}
return
}
stringWithValue := strings.Replace(dim.Targeted[0], " ", "", -1)
// handles device_type
if len(cf.lookup) == 1 &&
strings.Contains(dim.Targeted[0], cf.lookup[0]) &&
!strings.HasSuffix(dim.Targeted[0], "_") {
wurflFeatures[cf.logbrainNames[0]] = stringWithValue
}
// handles the browser, os, device_manufacturer
if len(cf.lookup) == 2 &&
strings.Contains(stringWithValue, cf.lookup[0]) {
splitted := strings.Split(stringWithValue, ":")
if !strings.HasSuffix(splitted[0], "_") {
wurflFeatures[cf.logbrainNames[0]] = splitted[0]
}
if len(splitted) == 2 && !strings.HasSuffix(stringWithValue, "_") {
wurflFeatures[cf.logbrainNames[1]] = stringWithValue
}
}
for featureName, featureVal := range wurflFeatures {
*appendTo = append(*appendTo,featureName+"^"+featureVal)
}
}
func getWurfulFeatureValues(raw []byte) []string {
var wd wurfulData
json.Unmarshal(raw, &wd)
var res []string
extractDim(&wd.Num24.Dim, "24", &res)
extractDim(&wd.Num25.Dim, "25", &res)
extractDim(&wd.Num26.Dim, "26", &res)
extractDim(&wd.Num29.Dim, "29", &res)
return res
}
func testEq(a, b []string) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for i := range a {
found := false
for j := range b {
if a[i] == b[j] {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func example1() {
rawWurflData := `{"24":{"1":{"targeted":["br_Firefox"],"untargeted":["br_Chrome Mobile:ve_67.0.3396"]}}}`
wurflFeatureValues := getWurfulFeatureValues([]byte(rawWurflData))
fmt.Println(wurflFeatureValues)
}
func example2() {
rawWurflData := `{}`
wurflFeatureValues := getWurfulFeatureValues([]byte(rawWurflData))
fmt.Println(wurflFeatureValues)
}
func example3() {
rawWurflData := `{"24":{"1":{"targeted":[],"untargeted":["br_Chrome Mobile:ve_67.0.3396"]}},"25":{"1":{"targeted":[],"untargeted":["os_Android:ve_8.1.0"]}},"29":{"1":{"targeted":[],"untargeted":["ma_Generic:mo_Android 2.0"]}},"26":{"1":{"targeted":[],"untargeted":["fo_Feature Phone"]}}}`
wurflFeatureValues := getWurfulFeatureValues([]byte(rawWurflData))
fmt.Println(wurflFeatureValues)
}
func example4() {
rawWurflData := `{"24":{"1":{"targeted":[],"untargeted":["br_"]}},"25":{"1":{"targeted":[],"untargeted":["os_Android:ve_"]}},"29":{"1":{"targeted":[],"untargeted":["ma_:mo_"]}},"26":{"1":{"targeted":[],"untargeted":["fo_Feature Phone"]}}}`
wurflFeatureValues := getWurfulFeatureValues([]byte(rawWurflData))
fmt.Println(wurflFeatureValues)
}
func main() {
example1()
example2()
example3()
example4()
}