Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Common Serialization Interface #25

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Serde"
uuid = "db9b398d-9517-45f8-9a95-92af99003e0e"
version = "2.0.0"
version = "3.0.0"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Expand Down
8 changes: 8 additions & 0 deletions src/Ser/Ser.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Ser/Ser

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

(ignore_field(::Type{T}, ::Val{x})::Bool) where {T,x} = false
(ignore_field(::Type{T}, k::Val{x}, v::V)::Bool) where {T,x,V} = ignore_field(T, k)
(ignore_null(::Type{T})::Bool) where {T} = true

include("SerCsv.jl")
using .SerCsv

Expand Down
14 changes: 5 additions & 9 deletions src/Ser/SerJson.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export to_json
export to_pretty_json

using Dates
import ..ser_name,
..ser_value,
..ser_type,
..ignore_null,
..ignore_field

const JSON_NULL = "null"
const INDENT = " "
Expand Down Expand Up @@ -155,20 +160,11 @@ function json_value!(buf::IOBuffer, f::Function, val::AbstractSet; l::Int64, kw.
return print(buf, indent(l - 1), "]")
end

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

(isnull(::Any)::Bool) = false
(isnull(v::Missing)::Bool) = true
(isnull(v::Nothing)::Bool) = true
(isnull(v::Float64)::Bool) = isnan(v) || isinf(v)

(ignore_null(::Type{T})::Bool) where {T} = false

(ignore_field(::Type{T}, ::Val{x})::Bool) where {T,x} = false
(ignore_field(::Type{T}, k::Val{x}, v::V)::Bool) where {T,x,V} = ignore_field(T, k)

function json_value!(buf::IOBuffer, f::Function, val::T; l::Int64, kw...)::Nothing where {T}
next = iterate(f(T))
print(buf, "{", indent(l))
Expand Down
12 changes: 7 additions & 5 deletions src/Ser/SerQuery.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ module SerQuery

export to_query

import ..ser_name,
..ser_value,
..ser_type,
..ignore_null,
..ignore_field

function _bytes end
function escape_query end

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

(ignore_null(::Type{T})::Bool) where {T} = true

isnull(::Any)::Bool = false
Expand Down Expand Up @@ -94,7 +96,7 @@ end
function iter_query(f::Function, query::Q)::Nothing where {Q}
for field in fieldnames(Q)
v = ser_type(Q, ser_value(Q, Val(field), getfield(query, field)))
if ignore_null(Q) && isnull(v)
if ignore_null(Q) && isnull(v) || ignore_field(Q, Val(field), v)
continue
end
field = string(ser_name(Q, Val(field)))
Expand Down
13 changes: 6 additions & 7 deletions src/Ser/SerToml.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module SerToml
export to_toml

using Dates
import ..ser_name,
..ser_value,
..ser_type,
..ignore_null,
..ignore_field

struct TomlSerializationError <: Exception
message::String
Expand Down Expand Up @@ -139,23 +144,17 @@ function toml_pairs(val::AbstractDict; kw...)
return sort([(k, v) for (k, v) in val], by = x -> !issimple(x[2]))
end

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

isnull(::Any) = false
isnull(v::Missing)::Bool = true
isnull(v::Nothing)::Bool = true

(ignore_null(::Type{T})::Bool) where {T} = true

function toml_pairs(val::T; kw...) where {T}
kv = Tuple[]

for field in fieldnames(T)
k = ser_name(T, Val(field))
v = ser_type(T, ser_value(T, Val(field), getfield(val, field)))
if ignore_null(T) && isnull(v)
if (isnull(v) || ignore_field(T, Val(field), v))
continue
end
push!(kv, (k, v))
Expand Down
55 changes: 27 additions & 28 deletions src/Ser/SerXml.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module SerXml
export to_xml

using Dates
import ..ser_name,
..ser_value,
..ser_type,
..ignore_null,
..ignore_field

const CONTENT_WORD = "_"

Expand Down Expand Up @@ -74,23 +79,23 @@ xml_key(val::Symbol; kw...)::String = xml_key(string(val); kw...)
# pair

function xml_pair(key, val::AbstractString; level::Int64, kw...)::String
return shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
return shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
"</" * xml_key(key; kw...) * ">" * "\n"
end

function xml_pair(key, val::Symbol; level::Int64, kw...)::String
return shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
"</" * xml_key(key; kw...) * ">" * "\n"
end

function xml_pair(key, val::Number; level::Int64, kw...)::String
return shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
return shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(val) *
"</" * xml_key(key; kw...) * ">" * "\n"
end

Expand All @@ -99,10 +104,10 @@ function xml_pair(key, val::AbstractVector{T}; level::Int64, kw...)::String wher
for el in val
if issimple(el)
push!(
buf,
shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(el) *
buf,
shift(level) *
"<" * xml_key(key; kw...) * ">" *
xml_value(el) *
"</" * xml_key(key; kw...) * ">" * "\n",
)
else
Expand All @@ -116,10 +121,10 @@ function xml_pair(key, val::AbstractDict; level::Int64, kw...)::String
tags, cont = nodes(val), content(val)
return if isempty(tags) && isempty(cont)
shift(level) * "<" * xml_key(key; kw...) * attribute_xml(val) * "/>" * "\n"
elseif isempty(cont)
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" *
"\n" * _to_xml(tags; level = level + 1) * shift(level) *
elseif isempty(cont)
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" *
"\n" * _to_xml(tags; level = level + 1) * shift(level) *
"</" * xml_key(key; kw...) * ">" * "\n"
else
shift(level) *
Expand All @@ -133,29 +138,23 @@ function xml_pairs(val::AbstractDict; kw...)
return [(k, v) for (k, v) in val]
end

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

(isnull(::Any)::Bool) = false
(isnull(v::Missing)::Bool) = true
(isnull(v::Nothing)::Bool) = true

(ignore_null(::Type{T})::Bool) where {T} = true

function xml_pair(key, val::T; level::Int64, kw...)::String where {T}
tags, cont = nodes(val), content(val)
return if isempty(tags) && isempty(cont)
shift(level) * "<" * xml_key(key; kw...) * attribute_xml(val) * "/>" * "\n"
elseif isempty(cont)
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" * "\n" *
_to_xml(tags; level = level + 1) * shift(level) *
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" * "\n" *
_to_xml(tags; level = level + 1) * shift(level) *
"</" * xml_key(key; kw...) * ">" * "\n"
else
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" *
cont * _to_xml(tags; level = level + 1) *
shift(level) *
"<" * xml_key(key; kw...) * attribute_xml(val) * ">" *
cont * _to_xml(tags; level = level + 1) *
"</" * xml_key(key; kw...) * ">" * "\n"
end
end
Expand Down
14 changes: 5 additions & 9 deletions src/Ser/SerYaml.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module SerYaml
export to_yaml

using Dates
import ..ser_name,
..ser_value,
..ser_type,
..ignore_null,
..ignore_field

const YAML_NULL = "null"
const INDENT = " "
Expand Down Expand Up @@ -186,20 +191,11 @@ function yaml_value!(buf::IOBuffer, f::Function, val::AbstractSet; l::Int64, ski
return print(buf)
end

(ser_name(::Type{T}, ::Val{x})::Symbol) where {T,x} = x
(ser_value(::Type{T}, ::Val{x}, v::V)::V) where {T,x,V} = v
(ser_type(::Type{T}, v::V)::V) where {T,V} = v

(isnull(::Any)::Bool) = false
(isnull(v::Missing)::Bool) = true
(isnull(v::Nothing)::Bool) = true
(isnull(v::Float64)::Bool) = isnan(v) || isinf(v)

(ignore_null(::Type{T})::Bool) where {T} = false

(ignore_field(::Type{T}, ::Val{x})::Bool) where {T,x} = false
(ignore_field(::Type{T}, k::Val{x}, v::V)::Bool) where {T,x,V} = ignore_field(T, k)

function yaml_value!(buf::IOBuffer, f::Function, val::T; l::Int64, skip_lf::Bool = false, kw...)::Nothing where {T}
next = iterate(f(T))
print(buf, skip_lf ? "" : indent(l))
Expand Down
10 changes: 5 additions & 5 deletions src/Utl/Macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ function chain(::Val{Symbol("@de_name")}, struct_name::Symbol, field_key::String
end
end

function chain(::Val{Symbol("@ser_json_name")}, struct_name::Symbol, field_key::String, ser_custom_name::Any)::Expr
function chain(::Val{Symbol("@ser_name")}, struct_name::Symbol, field_key::String, ser_custom_name::Any)::Expr
return quote
function Serde.SerJson.ser_name(::Type{T}, ::Val{Symbol($field_key)}) where {T<:$struct_name}
function Serde.ser_name(::Type{T}, ::Val{Symbol($field_key)}) where {T<:$struct_name}
return $ser_custom_name
end
end
Expand All @@ -31,7 +31,7 @@ Helper macro that implements user friendly configuration of the (de)serializatio
Available `decorators`:
- `@default_value`: Used to define default values for fields of declared type (see [`Serde.default_value`](@ref)).
- `@de_name`: Used to defines an alias names for fields of declared type (see [`Serde.custom_name`](@ref)).
- `@ser_json_name`: Used to define custom output name for fields of declared type (see [`Serde.ser_name`](@ref ser_name)).
- `@ser_name`: Used to define custom output name for fields of declared type (see [`Serde.ser_name`](@ref ser_name)).
Next, the syntax template looks like this:
```julia
@serde @decor_1 @decor_2 ... struct
Expand All @@ -46,7 +46,7 @@ Decorator values belonging to a certain field must be separated by the `|` symbo

## Examples
```julia
@serde @default_value @de_name @ser_json_name mutable struct Foo
@serde @default_value @de_name @ser_name mutable struct Foo
bar::Int64 | 1 | "first" | "bar"
baz::Int64 | 2 | "baz" | "second"
end
Expand All @@ -62,7 +62,7 @@ Also, now names from the `@de_name` column will be used for deserialization.
julia> deser_json(Foo, \"\"\"{"first": 30}\"\"\")
Foo(30, 2)
```
Names from the `@ser_json_name` column will be used as output names for serialization.
Names from the `@ser_name` column will be used as output names for serialization.
```julia-repl
julia> to_json(Foo(40, 50)) |> print
{"bar":40,"second":50}
Expand Down
Loading