1<?php 2 3namespace JMS\Serializer; 4 5use JMS\Serializer\Accessor\AccessorStrategyInterface; 6use JMS\Serializer\Metadata\ClassMetadata; 7use JMS\Serializer\Metadata\PropertyMetadata; 8use JMS\Serializer\Naming\AdvancedNamingStrategyInterface; 9use JMS\Serializer\Naming\PropertyNamingStrategyInterface; 10use JMS\Serializer\Util\Writer; 11use Symfony\Component\Yaml\Inline; 12 13/** 14 * Serialization Visitor for the YAML format. 15 * 16 * @see http://www.yaml.org/spec/ 17 * @author Johannes M. Schmitt <schmittjoh@gmail.com> 18 */ 19class YamlSerializationVisitor extends AbstractVisitor 20{ 21 public $writer; 22 23 private $navigator; 24 private $stack; 25 private $metadataStack; 26 private $currentMetadata; 27 28 public function __construct($namingStrategy, AccessorStrategyInterface $accessorStrategy = null) 29 { 30 parent::__construct($namingStrategy, $accessorStrategy); 31 32 $this->writer = new Writer(); 33 } 34 35 public function setNavigator(GraphNavigator $navigator) 36 { 37 $this->navigator = $navigator; 38 $this->writer->reset(); 39 $this->stack = new \SplStack; 40 $this->metadataStack = new \SplStack; 41 } 42 43 public function visitNull($data, array $type, Context $context) 44 { 45 if ('' === $this->writer->content) { 46 $this->writer->writeln('null'); 47 } 48 49 return 'null'; 50 } 51 52 public function visitString($data, array $type, Context $context) 53 { 54 $v = Inline::dump($data); 55 56 if ('' === $this->writer->content) { 57 $this->writer->writeln($v); 58 } 59 60 return $v; 61 } 62 63 /** 64 * @param array $data 65 * @param array $type 66 */ 67 public function visitArray($data, array $type, Context $context) 68 { 69 $isHash = isset($type['params'][1]); 70 71 $count = $this->writer->changeCount; 72 $isList = (isset($type['params'][0]) && !isset($type['params'][1])) 73 || array_keys($data) === range(0, \count($data) - 1); 74 75 foreach ($data as $k => $v) { 76 if (null === $v && $context->shouldSerializeNull() !== true) { 77 continue; 78 } 79 80 if ($isList && !$isHash) { 81 $this->writer->writeln('-'); 82 } else { 83 $this->writer->writeln(Inline::dump($k) . ':'); 84 } 85 86 $this->writer->indent(); 87 88 if (null !== $v = $this->navigator->accept($v, $this->getElementType($type), $context)) { 89 $this->writer 90 ->rtrim(false) 91 ->writeln(' ' . $v); 92 } 93 94 $this->writer->outdent(); 95 } 96 97 if ($count === $this->writer->changeCount && isset($type['params'][1])) { 98 $this->writer 99 ->rtrim(false) 100 ->writeln(' {}'); 101 } elseif (empty($data)) { 102 $this->writer 103 ->rtrim(false) 104 ->writeln(' []'); 105 } 106 } 107 108 public function visitBoolean($data, array $type, Context $context) 109 { 110 $v = $data ? 'true' : 'false'; 111 112 if ('' === $this->writer->content) { 113 $this->writer->writeln($v); 114 } 115 116 return $v; 117 } 118 119 public function visitDouble($data, array $type, Context $context) 120 { 121 $v = (string)$data; 122 123 if ('' === $this->writer->content) { 124 $this->writer->writeln($v); 125 } 126 127 return $v; 128 } 129 130 public function visitInteger($data, array $type, Context $context) 131 { 132 $v = (string)$data; 133 134 if ('' === $this->writer->content) { 135 $this->writer->writeln($v); 136 } 137 138 return $v; 139 } 140 141 public function startVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context) 142 { 143 } 144 145 public function visitProperty(PropertyMetadata $metadata, $data, Context $context) 146 { 147 $v = $this->accessor->getValue($data, $metadata); 148 149 if ((null === $v && $context->shouldSerializeNull() !== true) 150 || (true === $metadata->skipWhenEmpty && ($v instanceof \ArrayObject || \is_array($v)) && 0 === count($v)) 151 ) { 152 return; 153 } 154 155 if ($this->namingStrategy instanceof AdvancedNamingStrategyInterface) { 156 $name = $this->namingStrategy->getPropertyName($metadata, $context); 157 } else { 158 $name = $this->namingStrategy->translateName($metadata); 159 } 160 161 if (!$metadata->inline) { 162 $this->writer 163 ->writeln(Inline::dump($name) . ':') 164 ->indent(); 165 } 166 167 $this->setCurrentMetadata($metadata); 168 169 $count = $this->writer->changeCount; 170 171 if (null !== $v = $this->navigator->accept($v, $metadata->type, $context)) { 172 $this->writer 173 ->rtrim(false) 174 ->writeln(' ' . $v); 175 } elseif ($count === $this->writer->changeCount && !$metadata->inline) { 176 $this->writer->revert(); 177 } 178 179 if (!$metadata->inline) { 180 $this->writer->outdent(); 181 } 182 $this->revertCurrentMetadata(); 183 } 184 185 public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context) 186 { 187 } 188 189 public function setCurrentMetadata(PropertyMetadata $metadata) 190 { 191 $this->metadataStack->push($this->currentMetadata); 192 $this->currentMetadata = $metadata; 193 } 194 195 public function revertCurrentMetadata() 196 { 197 return $this->currentMetadata = $this->metadataStack->pop(); 198 } 199 200 public function getNavigator() 201 { 202 return $this->navigator; 203 } 204 205 public function getResult() 206 { 207 return $this->writer->getContent(); 208 } 209} 210