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

Filter the JSON based on a Value #175

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ end
In case both `:only` and `:except` keys are defined, the `:except` option is
ignored.

It is also possible to exclude all attributes with a specific value. This can be useful in the case where `Null` might mean something to an API and you don't want to send the attribute at all.

```elixir
defmodule SparsePerson do
@derive {Poison.Encoder, redact: :empty}
defstruct name: :empty, age: :empty
end
```

The `:redact` option can be included with `:only` and `:except`

### Key Validation

According to [RFC 7159][4] keys in a JSON object should be unique. This is
Expand Down
28 changes: 25 additions & 3 deletions lib/poison/encoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -379,25 +379,47 @@ defimpl Poison.Encoder, for: Any do
def deriving(module, _struct, options) do
only = options[:only]
except = options[:except]
redact = options[:redact]

extractor =
cond do
only ->
quote(do: Map.take(struct, unquote(only)))
quote(
do: json_redact(Map.take(struct, unquote(only)), unquote(redact))
)

except ->
except = [:__struct__ | except]
quote(do: Map.drop(struct, unquote(except)))

quote(
do: json_redact(Map.drop(struct, unquote(except)), unquote(redact))
)

true ->
quote(do: :maps.remove(:__struct__, struct))
quote(
do: json_redact(:maps.remove(:__struct__, struct), unquote(redact))
)
end

quote do
defimpl Encoder, for: unquote(module) do
def encode(struct, options) do
Encoder.Map.encode(unquote(extractor), options)
end

def json_redact(struct, nil), do: struct

def json_redact(struct, target) do
Enum.reduce(struct, %{}, fn {key, value}, redacted ->
case value do
^target ->
redacted

value ->
Map.put(redacted, key, value)
end
end)
end
end
end
end
Expand Down
42 changes: 42 additions & 0 deletions test/poison/encoder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,21 @@ defmodule Poison.EncoderTest do
defstruct name: "", size: 0
end

defmodule DerivedUsingRedact do
@derive {Poison.Encoder, redact: :empty}
defstruct name: :empty, size: 0
end

defmodule DerivedUsingOnlyAndRedact do
@derive {Poison.Encoder, only: [:name, :size], redact: :empty}
defstruct name: "", size: :empty, shape: "tirangle"
end

defmodule DerivedUsingExceptAndRedact do
@derive {Poison.Encoder, except: [:name], redact: :empty}
defstruct name: "", size: 10, shape: :empty
end

defmodule NonDerived do
defstruct name: ""
end
Expand All @@ -221,6 +236,33 @@ defmodule Poison.EncoderTest do
}

assert Poison.decode!(to_json(derived_using_except)) == %{"size" => 10}

derived_using_redact = %DerivedUsingRedact{
name: :empty,
size: 10
}

assert Poison.decode!(to_json(derived_using_redact)) == %{"size" => 10}

derived_using_only_and_redact = %DerivedUsingOnlyAndRedact{
name: "test",
size: :empty,
shape: "tirangle"
}

assert Poison.decode!(to_json(derived_using_only_and_redact)) == %{
"name" => "test"
}

derived_using_except_and_redact = %DerivedUsingExceptAndRedact{
name: "test",
size: 10,
shape: :empty
}

assert Poison.decode!(to_json(derived_using_except_and_redact)) == %{
"size" => 10
}
end

test "EncodeError" do
Expand Down