nu_command/formats/to/
json.rs1use nu_engine::command_prelude::*;
2use nu_protocol::{FromValue, PipelineMetadata};
3
4#[derive(Clone)]
5pub struct ToJson;
6
7impl Command for ToJson {
8 fn name(&self) -> &str {
9 "to json"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("to json")
14 .input_output_types(vec![(Type::Any, Type::String)])
15 .switch(
16 "raw",
17 "Remove all of the whitespace and trailing line ending.",
18 Some('r'),
19 )
20 .named(
21 "indent",
22 SyntaxShape::Number,
23 "Specify indentation width.",
24 Some('i'),
25 )
26 .named(
27 "tabs",
28 SyntaxShape::Number,
29 "Specify indentation tab quantity.",
30 Some('t'),
31 )
32 .switch(
33 "serialize",
34 "Serialize nushell types that cannot be deserialized.",
35 Some('s'),
36 )
37 .category(Category::Formats)
38 }
39
40 fn description(&self) -> &str {
41 "Converts table data into JSON text."
42 }
43
44 fn run(
45 &self,
46 engine_state: &EngineState,
47 stack: &mut Stack,
48 call: &Call,
49 input: PipelineData,
50 ) -> Result<PipelineData, ShellError> {
51 let raw = call.has_flag(engine_state, stack, "raw")?;
52 let use_tabs = call.get_flag(engine_state, stack, "tabs")?;
53 let indent = call.get_flag(engine_state, stack, "indent")?;
54 let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
55
56 let span = call.head;
57 let input = input.try_expand_range()?;
59 let value = input.into_value(span)?;
60 let ty = value.get_type();
61 let json_value = value_to_json_value(engine_state, value, span, serialize_types)?;
62
63 let json_result = if raw {
64 nu_json::to_string_raw(&json_value)
65 } else if let Some(tab_count) = use_tabs {
66 nu_json::to_string_with_tab_indentation(&json_value, tab_count)
67 } else if let Some(indent) = indent {
68 nu_json::to_string_with_indent(&json_value, indent)
69 } else {
70 nu_json::to_string(&json_value)
71 };
72
73 match json_result {
74 Ok(serde_json_string) => {
75 let res = Value::string(serde_json_string, span);
76 let metadata = PipelineMetadata {
77 data_source: nu_protocol::DataSource::None,
78 content_type: Some(mime::APPLICATION_JSON.to_string()),
79 ..Default::default()
80 };
81 Ok(PipelineData::value(res, Some(metadata)))
82 }
83 _ => Err(ShellError::CantConvert {
84 to_type: "JSON".into(),
85 from_type: ty.to_string(),
86 span,
87 help: None,
88 }),
89 }
90 }
91
92 fn examples(&self) -> Vec<Example<'_>> {
93 vec![
94 Example {
95 description: "Outputs a JSON string, with default indentation, representing the contents of this table.",
96 example: "[a b c] | to json",
97 result: Some(Value::test_string("[\n \"a\",\n \"b\",\n \"c\"\n]")),
98 },
99 Example {
100 description: "Outputs a JSON string, with 4-space indentation, representing the contents of this table.",
101 example: "[Joe Bob Sam] | to json --indent 4",
102 result: Some(Value::test_string(
103 "[\n \"Joe\",\n \"Bob\",\n \"Sam\"\n]",
104 )),
105 },
106 Example {
107 description: "Outputs an unformatted JSON string representing the contents of this table.",
108 example: "[1 2 3] | to json -r",
109 result: Some(Value::test_string("[1,2,3]")),
110 },
111 ]
112 }
113}
114
115pub fn value_to_json_value(
116 engine_state: &EngineState,
117 v: Value,
118 call_span: Span,
119 serialize_types: bool,
120) -> Result<nu_json::Value, ShellError> {
121 let value_span = v.span();
122 match serialize_types {
123 false => nu_json::Value::from_value(v),
124 true => nu_json::Value::from_value_serialized(v, engine_state)
125 }.map_err(|err| match err {
126 ShellError::CantConvert { from_type, .. } if from_type == "closure" => ShellError::UnsupportedInput {
127 msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
128 input: "value originates from here".into(),
129 msg_span: call_span,
130 input_span: value_span,
131 },
132 err => err
133 })
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 #[test]
141 fn test_examples() -> nu_test_support::Result {
142 nu_test_support::test().examples(ToJson)
143 }
144}