Как напечатать объекты JSON в AWK - PullRequest
0 голосов
/ 23 мая 2018

Я искал некоторые встроенные функции внутри awk, чтобы легко генерировать объекты JSON.Я наткнулся на несколько ответов и решил создать свой собственный.

Я хотел бы сгенерировать JSON из многомерных массивов, где я храню данные стиля таблицы, и использовать отдельное и динамическое определение схемы JSON, которая будет сгенерирована изэти данные.

Требуемый вывод:

{
"Name": JanA
"Surname": NowakA
"ID": 1234A
"Role": PrezesA
}
{
"Name": JanD
"Surname": NowakD
"ID": 12341D
"Role": PrezesD
}
{
"Name": JanC
"Surname": NowakC
"ID": 12342C
"Role": PrezesC
}

Входной файл:

pierwsza linia
druga linia
trzecia linia

dane wspólników
imie JanA
nazwisko NowakA
pesel 11111111111A
funkcja PrezesA

imie Ja"nD
nazwisko NowakD
pesel 11111111111
funkcja PrezesD

imie JanC
nazwisko NowakC
pesel 12342C
funkcja PrezesC

czwarta linia

reprezentanci

imie Tomek

На основе входного файла я создал многомерный массив:

JanA  NowaA 1234A PrezesA
JanD  NowakD 12341D PrezesD
JanC  NowakC 12342C PrezesC

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Не могли бы вы попробовать awk.

awk '/imie/ && value{print value;value=count=""} /imie/ || /nazwisko/ || /pesel/ || /funkcja/{value=value?value OFS $NF:$NF;count++} END{if(value && count==4){print value}}'  Input_file

Добавление формы решения, не относящейся к одному вкладышу, тоже сейчас.

awk '
/imie/ && value{
  print value;
  value=count=""}
/imie/ || /nazwisko/ || /pesel/ || /funkcja/{
  value=value?value OFS $NF:$NF;
  count++}
END{
  if(value && count==4){
    print value}
}'   Input_file
0 голосов
/ 23 мая 2018

Я сделаю удар в решение проблемы тараканов.Отступы не идеальны, а результаты не упорядочены (см. Примечание «Сортировка» ниже), но, по крайней мере, они способны рекурсивно обходить истинный многомерный массив и должны генерировать допустимый синтаксический анализатор JSON из любого массива. Бонус: массив данных - это схема.Ключи массива становятся ключами JSON.Нет необходимости создавать отдельный массив схем в дополнение к массиву данных.

Просто убедитесь, что используется истинное многомерное array[d1][d2][d3]... соглашение о построении массива данных, а не объединенный индекс array[d1,d2,d3...] соглашение.

Обновление:

У меня обновленный скрипт JSON gawk, опубликованный как GitHub Gist .Хотя приведенный ниже сценарий проверен на работу с данными OP, Я мог бы внести улучшения с тех пор, как последний пост был отредактирован. Пожалуйста, ознакомьтесь с Gist для наиболее тщательно протестированной версии с ошибками.

#!/usr/bin/gawk -f

BEGIN { IGNORECASE = 1 }

$1 ~ "imie" { record[++idx]["name"] = $2 }
$1 ~ "nazwisko" { record[idx]["surname"] = $2 }
$1 ~ "pesel" { record[idx]["ID"] = $2 }
$1 ~ "funkcja" { record[idx]["role"] = $2 }

END { print serialize(record, "\t") }

# ==== FUNCTIONS ====

function join(arr, sep, _p, i) {
    # syntax: join(array, string separator)
    # returns a string

    for (i in arr) {
        _p["result"] = _p["result"] ~ "[[:print:]]" ? _p["result"] sep arr[i] : arr[i]
    }
    return _p["result"]
}

function quote(str) {
    gsub(/\\/, "\\\\", str)
    gsub(/\r/, "\\r", str)
    gsub(/\n/, "\\n", str)
    gsub(/\t/, "\\t", str)
    return "\"" str "\""
}

function serialize(arr, indent_with, depth, _p, i, idx) {
    # syntax: serialize(array of arrays, indent string)
    # returns a JSON formatted string

    # sort arrays on key, ensures [...] values remain properly ordered
    if (!PROCINFO["sorted_in"]) PROCINFO["sorted_in"] = "@ind_num_asc"

    # determine whether array is indexed or associative
    for (i in arr) {
        _p["assoc"] = or(_p["assoc"], !(++_p["idx"] in arr))
    }

    # if associative, indent
    if (_p["assoc"]) {
        for (i = ++depth; i--;) {
            _p["end"] = _p["indent"]; _p["indent"] = _p["indent"] indent_with
        }
    }

    for (i in arr) {
        # If key length is 0, assume its an empty object
        if (!length(i)) return "{}"

        # quote key if not already quoted
        _p["key"] = i !~ /^".*"$/ ? quote(i) : i

        if (isarray(arr[i])) {
            if (_p["assoc"]) {
                _p["json"][++idx] = _p["indent"] _p["key"] ": " \
                    serialize(arr[i], indent_with, depth)
            } else {
                # if indexed array, dont print keys
                _p["json"][++idx] = serialize(arr[i], indent_with, depth)
            }
        } else {
            # quote if not numeric, boolean, null, already quoted, or too big for match()
            if (!((arr[i] ~ /^[0-9]+([\.e][0-9]+)?$/ && arr[i] !~ /^0[0-9]/) ||
                arr[i] ~ /^true|false|null|".*"$/) || length(arr[i]) > 1000)
                arr[i] = quote(arr[i])

            _p["json"][++idx] = _p["assoc"] ? _p["indent"] _p["key"] ": " arr[i] : arr[i]
        }
    }

    # I trial and errored the hell out of this. Problem is, gawk cant distinguish between
    # a value of null and no value.  I think this hack is as close as I can get, although
    # [""] will become [].
    if (!_p["assoc"] && join(_p["json"]) == "\"\"") return "[]"

    # surround with curly braces if object, square brackets if array
    return _p["assoc"] ? "{\n" join(_p["json"], ",\n") "\n" _p["end"] "}" \
        : "[" join(_p["json"], ", ") "]"
}

Вывод, полученный из данных примера OP:

[{
        "ID": "1234A",
        "name": "JanA",
        "role": "PrezesA",
        "surname": "NowakA"
}, {
        "ID": "12341D",
        "name": "JanD",
        "role": "PrezesD",
        "surname": "NowakD"
}, {
        "ID": "12342C",
        "name": "JanC",
        "role": "PrezesC",
        "surname": "NowakC"
}, {
        "name": "Tomek"
}]

Сортировка

Несмотря на то, что результаты по умолчанию упорядочены способом, понятным только gawk, это возможно для gawkсортировать результаты по полю.Например, если вы хотите отсортировать поле идентификатора, добавьте эту функцию:

function cmp_ID(i1, v1, i2, v2) {
    if (!isarray(v1) && v1 ~ /"ID"/ ) {
        return v1 < v2 ? -1 : (v1 != v2)
    }
}

Затем вставьте эту строку в ваш END раздел над print serialize(record):

PROCINFO["sorted_in"] = "cmp_ID"

См. Управление обходом массива для получения дополнительной информации.

0 голосов
/ 23 мая 2018

Моя обновленная реализация awk простого матричного принтера с проверкой на основе регулярных выражений для каждого столбца (работает с использованием gawk):

function ltrim(s) { sub(/^[ \t]+/, "", s); return s }
function rtrim(s) { sub(/[ \t]+$/, "", s); return s }
function sTrim(s){
    return rtrim(ltrim(s));
}

function jsonEscape(jsValue) {
    gsub(/\\/, "\\\\", jsValue)
    gsub(/"/,  "\\\"", jsValue)
    gsub(/\b/, "\\b",  jsValue)
    gsub(/\f/, "\\f",  jsValue)
    gsub(/\n/, "\\n",  jsValue)
    gsub(/\r/, "\\r",  jsValue)
    gsub(/\t/, "\\t",  jsValue)

    return jsValue
}

function jsonStringEscapeAndWrap(jsValue) {
    return "\42" jsonEscape(jsValue) "\42"
}

function jsonPrint(contentArray, contentRowsCount, schemaArray){    
    result = ""
    schemaLength = length(schemaArray)
    for (x = 1; x <= contentRowsCount; x++) {
        result = result "{"
        for(y = 1; y <= schemaLength; y++){

            result = result "\42" sTrim(schemaArray[y]) "\42:" sTrim(contentArray[x, y])

             if(y < schemaLength){
                result = result ","
            }

        }        
        result = result "}"
        if(x < contentRowsCount){
            result = result ",\n"
        }
    }
    return result
}

function jsonValidateAndPrint(contentArray, contentRowsCount, schemaArray, schemaColumnsCount, errorArray){  
    result = ""
    errorsCount = 1
    for (x = 1; x <= contentRowsCount; x++) {
        jsonRow = "{"
        for(y = 1; y <= schemaColumnsCount; y++){

            regexValue = schemaArray[y, 2]
            jsonValue = sTrim(contentArray[x, y])
            isValid = jsonValue ~ regexValue            

            if(isValid == 0){
                errorArray[errorsCount, 1] = "\42" sTrim(schemaArray[y, 1]) "\42"
                errorArray[errorsCount, 2] = "\42Value " jsonValue " not match format: " regexValue " \42"
                errorArray[errorsCount, 3] = x
                errorsCount++
                jsonValue = "null"
            }            

            jsonRow = jsonRow "\42" sTrim(schemaArray[y, 1]) "\42:" jsonValue

            if(y < schemaColumnsCount){
                jsonRow =  jsonRow ","
            }
        }        
        jsonRow = jsonRow "}"
        result = result jsonRow

        if(x < contentRowsCount){
            result = result ",\n"
        }
    }

    return result
}

BEGIN{  
    rowsCount =1
    matchCount = 0
    errorsCount = 0
    shareholdersJsonSchema[1, 1] = "Imie"
    shareholdersJsonSchema[2, 1] = "Nazwisko"
    shareholdersJsonSchema[3, 1] = "PESEL"
    shareholdersJsonSchema[4, 1] = "Funkcja"

    shareholdersJsonSchema[1, 2] = "\\.*"
    shareholdersJsonSchema[2, 2] = "\\.*"
    shareholdersJsonSchema[3, 2] = "^[0-9]{11}$"
    shareholdersJsonSchema[4, 2] = "\\.*"


    errorsSchema[1] = "PropertyName"    
    errorsSchema[2] = "Message"
    errorsSchema[3] = "PositionIndex"

    resultSchema[1]= "ShareHolders"
    resultSchema[2]= "Errors"
}

/dane wspólników/,/czwarta linia/{      

    if(/imie/ || /nazwisko/ || /pesel/ || /funkcja/){

        if(/imie/){
            shareholdersArray[rowsCount, 1] = jsonStringEscapeAndWrap($2)
            matchCount++ 
        }
        if(/nazwisko/){ 
            shareholdersArray[rowsCount, 2] = jsonStringEscapeAndWrap($2)
            matchCount ++ 
        }
        if(/pesel/){             
            shareholdersArray[rowsCount, 3] = $2
            matchCount ++ 
        }
        if(/funkcja/){ 
            shareholdersArray[rowsCount, 4] = jsonStringEscapeAndWrap($2)
            matchCount ++
        }

        if(matchCount==4){
            rowsCount++
            matchCount = 0;
        }         
    } 
}

END{
    shareHolders = jsonValidateAndPrint(shareholdersArray, rowsCount - 1, shareholdersJsonSchema, 4, errorArray)

    shareHoldersErrors = jsonPrint(errorArray, length(errorArray) / length(errorsSchema), errorsSchema)

    resultArray[1,1] = "\n[\n" shareHolders "\n]\n"
    resultArray[1,2] = "\n[\n" shareHoldersErrors "\n]\n"

    resultJson = jsonPrint(resultArray, 1, resultSchema)
    print resultJson    
}

Выводит:

{"ShareHolders":
[
{"Imie":"JanA","Nazwisko":"NowakA","PESEL":null,"Funkcja":"PrezesA"},
{"Imie":"Ja\"nD","Nazwisko":"NowakD","PESEL":11111111111,"Funkcja":"PrezesD"},
{"Imie":"JanC","Nazwisko":"NowakC","PESEL":null,"Funkcja":"PrezesC"}
]
,"Errors":
[
{"PropertyName":"PESEL","Message":"Value 11111111111A not match format: ^[0-9]{11}$ ","PositionIndex":1},
{"PropertyName":"PESEL","Message":"Value 12342C not match format: ^[0-9]{11}$ ","PositionIndex":3}
]
}
...